{"record":{"id":"d7c37339a901718c","repo":"stablyai/orca","slug":"codex-app-server-session-already-timed-out","errorCode":null,"errorMessage":"codex app-server session already timed out","messagePattern":"codex app-server session already timed out","errorType":"exception","errorClass":"CodexAppServerTimeoutError","httpStatus":null,"severity":"error","filePath":"src/main/codex/codex-app-server-session.ts","lineNumber":233,"sourceCode":"\n  function notify(method: string, params?: Record<string, unknown>): void {\n    const payload: Record<string, unknown> = { method }\n    if (params !== undefined) {\n      payload.params = params\n    }\n    try {\n      sendLine(payload)\n    } catch {\n      // Notifications are fire-and-forget; a dead child fails the next request.\n    }\n  }\n\n  async function requestRpc(method: string, params?: Record<string, unknown>): Promise<unknown> {\n    if (spawnError) {\n      throw spawnError\n    }\n    if (timedOut) {\n      throw new CodexAppServerTimeoutError('codex app-server session already timed out')\n    }\n    if (exited) {\n      throw buildEarlyExitError()\n    }\n    const id = nextRequestId++\n    const response = await new Promise<JsonRpcResponse>((resolve, reject) => {\n      pending.set(id, { resolve, reject })\n      const payload: Record<string, unknown> = { method, id }\n      if (params !== undefined) {\n        payload.params = params\n      }\n      try {\n        sendLine(payload)\n      } catch (error) {\n        pending.delete(id)\n        reject(error instanceof Error ? error : new Error(String(error)))\n      }\n    })","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/codex/codex-app-server-session.ts#L215-L251","documentation":"Thrown as CodexAppServerTimeoutError inside requestRpc when the session-wide deadline already fired (timedOut === true) before a new RPC call is made. The deadline timer sets timedOut and SIGKILLs the codex child; any subsequent requestRpc in the same session short-circuits with this message instead of writing into a dead pipe.","triggerScenarios":"The body callback issued multiple sequential RPCs (initialize, hooks/list, config/batchWrite, hooks/list again) and an earlier await consumed the whole timeoutMs budget, so the next requestRpc sees timedOut already true; or the deadline fired during await and the next call detects it.","commonSituations":"A grant or heal body does several round-trips and the cumulative time exceeds invocation.timeoutMs; a slow first hooks/list eats the budget; the timeout was sized for fewer RPCs than the body performs.","solutions":["Raise invocation.timeoutMs to cover the total number of sequential RPCs the body performs (not just one).","Reduce the number of sequential awaits in the session body, or batch work (e.g., one config/batchWrite instead of several).","Profile each RPC to find the slow one and optimize the codex-side workload (fewer hooks, smaller config).","Start a fresh session for additional work rather than reusing a timed-out one."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Design the session body to fit within the deadline; track elapsed budget:\nconst start = Date.now()\nconst withinBudget = () => Date.now() - start < invocation.timeoutMs - RESERVE_MS\n// Structure the body so it stops issuing RPCs once withinBudget() is false.","typeGuard":"function isAlreadyTimedOutError(error: unknown): boolean {\n  return error instanceof Error && error.message === 'codex app-server session already timed out'\n}","tryCatchPattern":"try {\n  await runCodexAppServerSession(invocation, async (rpc) => {\n    // do all RPCs here; the session owns the deadline\n  })\n} catch (error) {\n  if (isCodexAppServerTimeoutError(error)) {\n    // start a fresh session with a larger timeoutMs rather than reusing\n  }\n}","preventionTips":["Set invocation.timeoutMs to exceed the total of all sequential RPCs in the body.","Batch edits (one config/batchWrite) instead of many sequential writes.","Start a new session for additional work rather than reusing an expired one.","Avoid issuing RPCs after a long await in the same session body."],"tags":["codex","app-server","timeout","deadline","json-rpc"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}