What's the correct way of detecting end of pipe for boost::asio::readable_pipe used with Process V2?
01:13 27 Apr 2026

I use Process V2 and read its output via boost::asio::readable_pipe. I need to detect whether the reading finished successfully by reaching end of data, or the reading ended because of an error. The behaviour however differs on Linux and Windows after successfully reaching end:

  • on Linux I get boost::system::error_code whose value() returns 2 and message is "End of file"

  • on Windows I get boost::system::error_code whose value() returns 109 and message is "The pipe has been ended"

Is there any other platform-independent way to check the end, that doesn't need having conditions on OS?

Here is a complete example:

#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 

int main()
{
    boost::asio::io_context ctx;
    boost::asio::readable_pipe stdOutPipe(ctx);

#ifdef unix
    std::string processName = "/usr/bin/bash";
    std::vector processArgs = { "-c", "ls", "-al"};
#else
    std::string processName = std::format("{}\\system32\\cmd.exe", std::getenv("windir"));
    std::vector processArgs = { "/c", "dir"};
#endif

    boost::process::v2::process process(ctx, processName, processArgs, boost::process::v2::process_stdio{ {}, stdOutPipe, {} });

    std::jthread printStdOutThread([&]
        {
            while (true)
            {
                std::string stdOut;
                boost::system::error_code ec;
                size_t stdOutLength = boost::asio::read(stdOutPipe, boost::asio::dynamic_buffer(stdOut), ec);

                if (stdOutLength > 0)
                {
                    if (stdOut.size() != stdOutLength)
                        stdOut.resize(stdOutLength);

                    std::cout << stdOut;
                    std::cout.flush();
                }

                if (ec.value() != boost::system::errc::success)
                {
                    std::cout
                        << "------------------------\n"
                        << "Pipe end.\n"
                        << "eof: " << (ec == boost::asio::error::eof) << "\n"      // Windows: 0, Linux: 1
                        << "error code: " << ec.value() << "\n"       // Windows: 109, Linux: 2
                        << "error message: " << ec.message() << "\n"     // Windows: "The pipe has been ended", Linux: "End of file"
                        << "pipe is still open: " << stdOutPipe.is_open() << "\n"     // Windows: 1, Linux: 1
                        << "process is still running: " << process.running() << "\n";    // Unreliable because the child process may crash or be terminated at any time, but got this: Windows: false, Linux: true
                    std::cout.flush();

                    break;
                }
            }
        });

    ctx.run();

    int resultCode = process.wait();

    return resultCode;
}
c++ boost-asio boost-process