{"record":{"id":"2f6d55675275e7fb","repo":"openclaw/openclaw","slug":"codex-thread-resume-outcome-is-indeterminate-for","errorCode":null,"errorMessage":"Codex thread/resume outcome is indeterminate for ${threadId}","messagePattern":"Codex thread/resume outcome is indeterminate for (.+?)","errorType":"exception","errorClass":"CodexAppServerUnsafeSubscriptionError","httpStatus":null,"severity":"critical","filePath":"extensions/codex/src/app-server/thread-resume.ts","lineNumber":56,"sourceCode":"      throw error;\n    }\n    if (error instanceof CodexAppServerRpcError) {\n      // A structured RPC error proves Codex rejected the resume, so the client\n      // holds no hidden subscription and can safely stay in the shared pool.\n      throw error;\n    }\n    try {\n      await params.abandonClient();\n    } catch (abandonError) {\n      throw new CodexAppServerUnsafeSubscriptionError(\n        `Codex thread/resume client could not be retired for ${threadId}`,\n        { cause: abandonError },\n      );\n    }\n    if (error instanceof CodexAppServerUnsafeSubscriptionError) {\n      throw error;\n    }\n    throw new CodexAppServerUnsafeSubscriptionError(\n      error instanceof Error\n        ? error.message\n        : `Codex thread/resume outcome is indeterminate for ${threadId}`,\n      { cause: error },\n    );\n  }\n  return response;\n}\n","sourceCodeStart":38,"sourceCodeEnd":65,"githubUrl":"https://github.com/openclaw/openclaw/blob/01804a75319da4b69c9ab98ceaa30477e22b8c0b/extensions/codex/src/app-server/thread-resume.ts#L38-L65","documentation":"Same thread/resume error path in resumeCodexAppServerThread, reached after the client was successfully abandoned. The caught value is not an Error instance (and not already a CodexAppServerUnsafeSubscriptionError), so there is no message to propagate; the outcome of the resume is genuinely unknown and is wrapped as an unsafe-subscription error.","triggerScenarios":"The resume promise rejects with a non-Error value (throw \"string\", throw 42, or Promise.reject with a plain object) and abandonClient() then succeeds.","commonSituations":"A transport or validator dependency that rejects with a string or plain object instead of a real Error; defensive coding in an upstream module that throws non-Error values.","solutions":["Identify the upstream component that rejected with a non-Error value and make it throw a real Error subclass.","After this error, reconnect the app-server client since the prior client was abandoned.","Log the raw cause to diagnose which dependency produced the non-Error rejection."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"import { CodexAppServerUnsafeSubscriptionError } from './app-server/attempt-client-cleanup.js';\nfunction isUnsafeSubscriptionError(e) { return e instanceof CodexAppServerUnsafeSubscriptionError; }","tryCatchPattern":"try { await resumeCodexAppServerThread({ client, abandonClient, request }); }\ncatch (e) {\n  if (e instanceof CodexAppServerUnsafeSubscriptionError && /indeterminate/.test(e.message)) {\n    logRawCause(e.cause); await reconnectAppServerClient();\n  }\n  throw e;\n}","preventionTips":["Ensure every dependency in the resume path rejects with a real Error, never a string or plain object.","Wrap third-party callbacks so they never throw non-Error values.","Treat indeterminate outcomes as reconnect triggers."],"tags":["codex","subscription-safety","error-handling","resume"],"backgroundTag":null,"analyzedSha":"01804a75319da4b69c9ab98ceaa30477e22b8c0b","analyzedAt":"2026-08-12T04:37:58.197Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}