{"record":{"id":"2f61c38490599634","repo":"expo/expo","slug":"failed-to-open-pipe-pipepath-for-writing-err","errorCode":null,"errorMessage":"Failed to open pipe ${pipePath} for writing: ${error.message}","messagePattern":"Failed to open pipe (.+?) for writing: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/bare-expo/e2e/image-comparison/inspector/ScreenInspectorIOS.ts","lineNumber":163,"sourceCode":"    request: InspectorRequest,\n    timeoutMs: number = 10000\n  ): Promise<void> {\n    const buffer = Buffer.from(JSON.stringify(request), 'utf8');\n    console.log(`📤 Sending request: ${buffer.toString()}`);\n\n    // Open the write end non-blocking: it fails with ENXIO while the inspector isn't reading\n    // (e.g. still busy with a previous request or not running), so poll with a deadline. A\n    // blocking open would park a file descriptor forever when the inspector is gone, and\n    // enough of those exhaust the fs thread pool and starve every later lookup.\n    const deadline = Date.now() + timeoutMs;\n    let fd: number | null = null;\n    try {\n      while (fd == null) {\n        try {\n          fd = fs.openSync(pipePath, fs.constants.O_WRONLY | fs.constants.O_NONBLOCK);\n        } catch (error: any) {\n          if (error.code !== 'ENXIO' || Date.now() >= deadline) {\n            throw new Error(`Failed to open pipe ${pipePath} for writing: ${error.message}`);\n          }\n          await new Promise((resolve) => setTimeout(resolve, 50));\n        }\n      }\n\n      const bytesWritten = fs.writeSync(fd, buffer);\n      if (bytesWritten !== buffer.length) {\n        throw new Error(\n          `Partial write to pipe ${pipePath}: ${bytesWritten}/${buffer.length} bytes`\n        );\n      }\n    } finally {\n      if (fd != null) {\n        fs.closeSync(fd);\n      }\n    }\n  }\n","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/expo/expo/blob/b09195aac27c9e4a63daaf12dcb6f00b6b2e08ee/apps/bare-expo/e2e/image-comparison/inspector/ScreenInspectorIOS.ts#L145-L181","documentation":"Thrown by writeToNamedPipe when opening the request FIFO for writing fails with an error other than ENXIO, or when ENXIO persists past the configured timeout. ENXIO is expected (the inspector dylib has not opened the read end yet) and is polled; any other errno — or ENXIO that outlasts the deadline — is fatal. The pattern avoids leaking file descriptors that would exhaust the libuv thread pool.","triggerScenarios":"The request named pipe (/tmp/ios_screen_inspector_request) does not exist (ENOENT) because the dylib never created it; a permission error (EACCES) on the pipe; the inspector process died and the pipe was removed mid-poll; or the inspector is alive but never opened the pipe within timeoutMs (default 10000).","commonSituations":"The app was not launched through startSimulatorAppWithDylib so the dylib is not running and the pipe is never created; /tmp is not writable or the pipe was left from a crashed run with wrong ownership; the simulator/app crashed between launch and the request; timeoutMs too short for a cold start.","solutions":["Ensure the app is launched via startSimulatorAppWithDylib(deviceID, bundleId) so the inspector dylib is injected and creates the request pipe.","Remove stale pipes (/tmp/ios_screen_inspector_request*) left by crashed runs before retrying.","Confirm the simulator is booted and the target app is the foreground process.","Raise timeoutMs on writeToNamedPipe / sendRequest if the dylib is slow to open the read end under load."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import { existsSync, statSync } from 'fs';\n\nfunction requestPipeReady(pipePath: string): boolean {\n  if (!existsSync(pipePath)) return false;\n  try {\n    return statSync(pipePath).isFIFO();\n  } catch {\n    return false;\n  }\n}\n// before calling sendRequest, ensure the dylib-created request FIFO exists\nif (!requestPipeReady('/tmp/ios_screen_inspector_request')) {\n  throw new Error('Inspector request pipe missing — is the app launched with the dylib?');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await inspector.getCoordinates(testID);\n} catch (e) {\n  if (e instanceof Error && /Failed to open pipe .* for writing/.test(e.message)) {\n    // dylib not running or pipe gone — relaunch the app with the dylib and retry once\n    await inspector.startSimulatorAppWithDylib(deviceID, bundleId);\n    return inspector.getCoordinates(testID);\n  }\n  throw e;\n}","preventionTips":["Always launch the app through startSimulatorAppWithDylib before issuing requests.","Clean stale pipes under /tmp/ios_screen_inspector_* at the start of each run.","Treat ENXIO-past-deadline as 'inspector not running' and relaunch rather than silently retrying."],"tags":["ios","named-pipe","fifo","filesystem","inspector","e2e"],"backgroundTag":null,"analyzedSha":"b09195aac27c9e4a63daaf12dcb6f00b6b2e08ee","analyzedAt":"2026-08-12T15:59:58.304Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}