{"record":{"id":"48d2992c96002934","repo":"stablyai/orca","slug":"codex-trust-grant-entry-produced-no-result-exit","errorCode":null,"errorMessage":"codex trust-grant entry produced no result (exit ${spawned.status ?? 'unknown'})${spawned.stderr ? `: ${spawned.stderr.trim().slice(0, 400)}` : ''}","messagePattern":"codex trust-grant entry produced no result \\(exit (.+?)\\)(.+?)` : ''\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/codex/codex-app-server-grant-bridge.ts","lineNumber":128,"sourceCode":"    throw spawned.error\n  }\n  if (spawned.signal) {\n    throw new CodexAppServerTimeoutError(\n      `codex trust-grant entry killed by ${spawned.signal} after ${request.invocation.timeoutMs}ms deadline`\n    )\n  }\n  const lines = (spawned.stdout ?? '').split('\\n').filter((line) => line.trim().length > 0)\n  const lastLine = lines.at(-1)\n  let envelope: GrantEntryEnvelope | null = null\n  if (lastLine) {\n    try {\n      envelope = JSON.parse(lastLine) as GrantEntryEnvelope\n    } catch {\n      envelope = null\n    }\n  }\n  if (!envelope) {\n    throw new Error(\n      `codex trust-grant entry produced no result (exit ${spawned.status ?? 'unknown'})${\n        spawned.stderr ? `: ${spawned.stderr.trim().slice(0, 400)}` : ''\n      }`\n    )\n  }\n  if (!envelope.ok) {\n    if (envelope.unsupported) {\n      throw new CodexAppServerUnsupportedError(envelope.message)\n    }\n    if (envelope.errorName === 'CodexAppServerTimeoutError') {\n      throw new CodexAppServerTimeoutError(envelope.message)\n    }\n    throw new Error(envelope.message)\n  }\n  return envelope.result\n}\n","sourceCodeStart":110,"sourceCodeEnd":145,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/codex/codex-app-server-grant-bridge.ts#L110-L145","documentation":"Thrown by the synchronous grant-bridge after it spawnSyncs the bundled ELECTRON_RUN_AS_NODE entry child and cannot parse a JSON envelope from the last non-empty stdout line. The bridge exists because hook-trust grant is synchronous launch prep, but the actual JSON-RPC session needs a live event loop, so it runs in a short-lived node child that must print exactly one JSON envelope on stdout. A missing/unparseable envelope means the entry crashed, was killed, or wrote garbage before it could report a structured result.","triggerScenarios":"The entry child exited non-zero (or was SIGKILLed by the spawnSync timeout margin) without writing a parseable final JSON line; stdout was empty; the entry threw before reaching buildGrantEntryEnvelope; maxBuffer (16MB) was exceeded truncating the envelope; or the entry printed log noise after the envelope so the 'last line' is not JSON.","commonSituations":"A codex CLI upgrade changed app-server handshake behavior so the entry's session throws early; the entry bundle is missing/corrupt in a packaged build (asar.unpacked path mismatch); the system is under heavy load so the entry exceeds timeoutMs+margin and is killed; antivirus or sandbox policy strips the child's stdout on Windows.","solutions":["Inspect the embedded exit status and first 400 chars of stderr in the message to classify the failure (non-zero exit vs killed vs empty stdout).","If exit is 'unknown' or shows SIGKILL, raise request.invocation.timeoutMs or the test-only timeoutMarginMs so the entry has room to finish and report.","If stderr shows a stack trace, reproduce by running the entry directly: ELECTRON_RUN_AS_NODE=1 node <resolveCodexGrantEntryPath()> with the request JSON on stdin.","Verify the packaged build emits the entry under app.asar.unpacked/out/main/codex (resolveCodexGrantEntryPath replaces app.asar with app.asar.unpacked).","If the entry prints non-JSON log lines after the envelope, ensure only buildGrantEntryEnvelope output goes to stdout and all diagnostics go to stderr."],"exampleFix":"// before: entry mixes a console.log after the envelope\nprocess.stdout.write(JSON.stringify(envelope) + '\\n')\nconsole.log('done') // makes the last stdout line 'done', not JSON\n\n// after: only the envelope touches stdout\ndefault: {\n  process.stderr.write('done\\n')\n  process.stdout.write(JSON.stringify(envelope) + '\\n')\n}","handlingStrategy":"try-catch","validationCode":"// Before calling runCodexHookTrustGrantSessionSync, verify the entry resolves:\nconst entryPath = resolveCodexGrantEntryPath()\nif (!entryPath || !existsSync(entryPath)) {\n  // skip the real-home lane; stay managed\n}","typeGuard":"function isGrantEntryNoResultError(error: unknown): boolean {\n  return error instanceof Error && error.message.startsWith('codex trust-grant entry produced no result')\n}","tryCatchPattern":"try {\n  const result = runCodexHookTrustGrantSessionSync(request)\n  // use result\n} catch (error) {\n  if (isGrantEntryNoResultError(error)) {\n    // inspect message for exit/stderr; fall back to managed lane, retry later\n    currentLane = 'unavailable'\n    installRetryAfterMs = Date.now() + CODEX_TRUST_GRANT_TRANSIENT_RETRY_INTERVAL_MS\n  } else {\n    throw error\n  }\n}","preventionTips":["Ensure the entry bundle is emitted and asar-unpacked in production builds.","Size invocation.timeoutMs + timeoutMarginMs to the worst-case session length.","Route all entry diagnostics to stderr so stdout's last line is always the JSON envelope.","Run the entry standalone during CI to catch crash-before-envelope regressions."],"tags":["codex","app-server","spawn","json-rpc","launch-prep","sync-bridge"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}