Why doesn't open() return an error when a pipe open fails?

It does, but probably not how you expect it to. On systems that follow the standard fork/exec paradigm (eg, Unix), it works like this: open causes a fork. In the parent, open returns with the process ID of the child. The child execs the command to be piped to/from. The parent can't know whether the exec was successful or not - all it can return is whether the fork succeeded or not. To find out if the command succeeded, you have to catch SIGCHLD and wait to get the exit status.

On systems that follow the spawn paradigm, open might do what you expect - unless perl uses a shell to start your command. In this case the fork/exec description still applies.