You are right, it is rookie programming, probably mine. Your statement: _anything_ that goes into ->fd[] must be treated as final as soon as you release the lock on ->fgrp is the crux of the matter. Your solution is reasonable. I would probably do something a little dirtier to avoid duplicating the functionality of newfd(). It would be just to make the waserror() a little smarter in syspipe(): if(waserror()){ if(fd[0] >= 0) fdclose(fd[0]) else cclose(c[0]); if(fd[1] >= 0) fdclose(fd[1]); else if(c[1] != nil) cclose(c[1]); nexterror(); } and leave the rest alone.