{"record":{"id":"92b853069ec70c5c","repo":"stablyai/orca","slug":"response-error-message-92b853","errorCode":null,"errorMessage":"${response.error.message}","messagePattern":"\\$\\{response\\.error\\.message\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"mobile/src/components/codex-reset-credit.ts","lineNumber":237,"sourceCode":"async function performCodexResetCreditRequest(\n  client: Pick<RpcClient, 'sendRequest'>,\n  options: {\n    hostId: string\n    expectedScope: CodexResetCreditExpectedScope\n    createIdempotencyKey: () => string\n  }\n): Promise<CodexResetCreditRequestResult> {\n  const attempt = await getOrCreateCodexResetAttempt(options)\n  const response = await client.sendRequest(\n    'accounts.consumeCodexResetCredit',\n    {\n      idempotencyKey: attempt.idempotencyKey,\n      expectedScope: attempt.expectedScope\n    },\n    { timeoutMs: RESET_RPC_TIMEOUT_MS }\n  )\n  if (!response.ok) {\n    throw new Error(response.error.message)\n  }\n  const result = decodeResetResult(response.result, attempt.expectedScope)\n  let attemptJournalRetained = false\n  try {\n    await clearCodexResetAttemptAfterAuthoritativeResponse({\n      hostId: options.hostId,\n      expectedScope: attempt.expectedScope,\n      idempotencyKey: attempt.idempotencyKey\n    })\n  } catch {\n    attemptJournalRetained = true\n  }\n  return { ...result, attemptJournalRetained }\n}\n\nexport async function requestCodexResetCredit(\n  client: Pick<RpcClient, 'sendRequest'>,\n  options: {","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/mobile/src/components/codex-reset-credit.ts#L219-L255","documentation":"Re-thrown host error from the accounts.consumeCodexResetCredit RPC. The RPC is wrapped with a 90s timeout (RESET_RPC_TIMEOUT_MS) and is idempotent (idempotencyKey + expectedScope), so a failure here is the host refusing or failing the reset — not a client bug. The thrown message is whatever the host put on response.error.message.","triggerScenarios":"client.sendRequest('accounts.consumeCodexResetCredit', ...) returns {ok:false}; common server-side reasons: the idempotencyKey was already redeemed with a different scope, the account has no reset credits, the host timed out at 90s, the transport disconnected mid-RPC, or the host rejected expectedScope as stale.","commonSituations":"Slow or saturated network to the desktop host pushing the consume call past 90s; the desktop's own provider call failing; an old idempotencyKey being replayed after the journal was partially cleared; the user's Codex account has zero credits.","solutions":["Inspect response.error.code/message before surfacing — 'alreadyRedeemed' style codes are safe to treat as success (the reset was applied), not as a hard failure.","On timeout-shaped errors, retry with the SAME idempotencyKey (the journal retains it) so the host deduplicates rather than double-consuming a credit.","On scope-mismatch errors, discard the journal attempt, recompute getCodexResetCreditScope from a fresh snapshot, and start over with a new idempotencyKey.","If the error is transport-level (disconnected), reconnect the host and retry the idempotent call once."],"exampleFix":"// before\nif (!response.ok) {\n  throw new Error(response.error.message)\n}\n\n// after — distinguish recoverable from terminal\nif (!response.ok) {\n  const code = response.error?.code\n  if (code === 'alreadyRedeemed' || code === 'reset_applied') {\n    return { outcome: 'alreadyRedeemed', scope: attempt.expectedScope, snapshot: await fetchAccountsSnapshot(client), attemptJournalRetained: false }\n  }\n  throw new Error(response.error.message)\n}","handlingStrategy":"retry","validationCode":"// Confirm the host is reachable and the scope is still redeemable\nif (connState !== 'connected') throw new Error('Host not connected')\nconst fresh = getCodexResetCreditScope(await fetchAccountsSnapshot(client))\nif (!fresh) throw new Error('No reset credit scope available')","typeGuard":"function isRetryableResetError(err: unknown): boolean {\n  if (!(err instanceof Error)) return false\n  const msg = err.message.toLowerCase()\n  return msg.includes('timeout') || msg.includes('disconnected') || msg.includes('temporarily unavailable')\n}","tryCatchPattern":"try {\n  return await performCodexResetCreditRequest(client, options)\n} catch (err) {\n  // The idempotencyKey makes retry safe for transient failures\n  if (isRetryableResetError(err)) {\n    return await performCodexResetCreditRequest(client, options) // same idempotencyKey\n  }\n  throw err\n}","preventionTips":["Always create the idempotencyKey via getOrCreateCodexResetAttempt so retries deduplicate on the host.","Differentiate 'alreadyRedeemed' style codes from hard failures before surfacing — a redeemed key means success.","Keep RESET_RPC_TIMEOUT_MS generous (90s) since the host provider call can be slow; do not shrink it for UI responsiveness."],"tags":["rpc","codex","rate-limit","idempotency","typescript"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}