{"record":{"id":"6cd00be63f69c770","repo":"heygen-com/hyperframes","slug":"background-preview-child-did-not-report-a-pid","errorCode":null,"errorMessage":"background preview child did not report a PID","messagePattern":"background preview child did not report a PID","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/cli/src/commands/previewLifecycle.ts","lineNumber":152,"sourceCode":"  const logPath = previewLogPath(projectDir, stateHome);\n  mkdirSync(dirname(logPath), { recursive: true });\n  const logFd = openSync(logPath, \"a\", 0o600);\n  const spawn = dependencies.spawn ?? (nodeSpawn as unknown as SpawnPreview);\n  let child: SpawnResult;\n  try {\n    child = spawn(\n      dependencies.execPath ?? process.execPath,\n      buildBackgroundPreviewArgs(dependencies.argv ?? process.argv.slice(1)),\n      {\n        detached: true,\n        stdio: [\"ignore\", logFd, logFd],\n        env: process.env,\n      },\n    );\n  } finally {\n    closeSync(logFd);\n  }\n  if (!child.pid) throw new Error(\"background preview child did not report a PID\");\n  child.unref();\n  return { pid: child.pid, logPath };\n}\n\nfunction startedServer(\n  servers: ActiveServer[],\n  projectDir: string,\n  existing: ActiveServer | null,\n  forceNew: boolean,\n  browserGpuMode?: BrowserGpuMode,\n): ActiveServer | null {\n  const candidates =\n    forceNew && existing ? servers.filter((server) => server.port !== existing.port) : servers;\n  return matchingServer(candidates, projectDir, browserGpuMode);\n}\n\nexport function buildBackgroundPreviewArgs(argv: string[]): string[] {\n  const filtered = argv.filter(","sourceCodeStart":134,"sourceCodeEnd":170,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/commands/previewLifecycle.ts#L134-L170","documentation":"Thrown by startBackgroundPreview after spawn() returns a child whose .pid is falsy. Node sets child.pid synchronously when the OS forks/execs the process; a missing pid means the process was never actually created (e.g. the executable path does not exist or the spawn failed at the OS level before a pid could be assigned).","triggerScenarios":"dependencies.execPath / process.execPath points to a non-existent or non-executable Node binary; the host is at a process/resource limit so fork fails; a broken Node install invoked via a shim that cannot exec. The detached spawn with stdio routed to a log fd succeeds in returning a Child handle but with pid undefined.","commonSituations":"Running under a container/sandbox that restricts fork; NODE path overridden to a bad value; very low ulimit -u (max processes); a corrupted or relocated Node installation.","solutions":["Verify the Node binary is executable: `node -v` and `which node`","Check process limits with `ulimit -u` and raise if at ceiling","If injecting dependencies.execPath in tests/host code, ensure it resolves to a real executable"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"import { existsSync } from \"node:fs\";\nfunction nodeExecutableOk(p: string): boolean {\n  try { return existsSync(p) && (p === process.execPath); } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await startBackgroundPreview(...);\n} catch (e) {\n  if (String(e.message).includes(\"did not report a PID\")) {\n    // verify node binary + raise process limits, then retry once\n  }\n  throw e;\n}","preventionTips":["Confirm `node -v` works in the exact environment that spawns the preview","Raise ulimit -u in containers/sandboxes before spawning detached children","Avoid overriding process.execPath to an unverified path"],"tags":["process-management","spawn","preview","system-resources"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}