{"record":{"id":"8016eb7022c98639","repo":"decolua/9router","slug":"a-redeem-request-id-is-required-to-consume-a-codex","errorCode":null,"errorMessage":"A redeem request id is required to consume a Codex reset credit.","messagePattern":"A redeem request id is required to consume a Codex reset credit\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"open-sse/services/usage/codex.js","lineNumber":186,"sourceCode":"\n  const credits = Array.isArray(data?.credits) ? data.credits : [];\n  return {\n    availableCount: Math.max(0, toFiniteNumber(data?.available_count ?? data?.availableCount, 0)),\n    credits: credits.map((credit) => ({\n      status: String(credit?.status || \"unknown\"),\n      grantedAt: toIsoDate(credit?.granted_at ?? credit?.grantedAt),\n      expiresAt: toIsoDate(credit?.expires_at ?? credit?.expiresAt),\n    })),\n  };\n}\n\n// Consume one Codex rate-limit reset credit (irreversible, spends 1 credit)\nexport async function consumeCodexRateLimitResetCredit(accessToken, redeemRequestId, proxyOptions = null) {\n  if (!accessToken) {\n    throw new Error(\"No Codex access token available. Please re-authorize the connection.\");\n  }\n  if (!redeemRequestId || typeof redeemRequestId !== \"string\") {\n    throw new Error(\"A redeem request id is required to consume a Codex reset credit.\");\n  }\n\n  let response;\n  let data = null;\n  try {\n    response = await proxyAwareFetch(CODEX_CONFIG.resetCreditsConsumeUrl, {\n      method: \"POST\",\n      headers: {\n        \"Authorization\": `Bearer ${accessToken}`,\n        \"Accept\": \"application/json\",\n        \"Content-Type\": \"application/json\",\n      },\n      body: JSON.stringify({ redeem_request_id: redeemRequestId }),\n    }, proxyOptions);\n\n    const text = await response.text();\n    data = text ? JSON.parse(text) : null;\n  } catch (error) {","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/services/usage/codex.js#L168-L204","documentation":"consumeCodexRateLimitResetCredit(accessToken, redeemRequestId) requires a redeem request id identifying which reset credit to spend. If redeemRequestId is missing or not a non-empty string, this guard error is thrown before any network call. No credit is consumed in this case.","triggerScenarios":"The consume handler is invoked without a redeemRequestId, with an empty string, or with a non-string value (e.g. a number or object from an unvalidated request body).","commonSituations":"Client UI calling the consume POST without first fetching available credits to obtain an id; request body field renamed or omitted; id coerced to a number by JSON handling.","solutions":["Fetch available credits first (getCodexRateLimitResetCredits) and pass one of the returned credit ids as redeemRequestId","Validate the client request body includes a string redeemRequestId before dispatching to the handler","Coerce/trim the incoming id and reject empty strings client-side","Check the credits response schema — take the correct id field from each credit object"],"exampleFix":"// before\nawait consumeCodexRateLimitResetCredit(token, body.id);\n// after\nif (typeof body.redeemRequestId !== 'string' || !body.redeemRequestId) {\n  return res.status(400).json({ error: 'redeemRequestId is required' });\n}\nawait consumeCodexRateLimitResetCredit(token, body.redeemRequestId);","handlingStrategy":"validation","validationCode":"// caller-side validation before invoking\nconst rid = body?.redeemRequestId;\nif (typeof rid !== 'string' || rid.trim() === '') {\n  return res.status(400).json({ error: 'redeemRequestId (string) is required' });\n}","typeGuard":"function isValidRedeemId(id) { return typeof id === 'string' && id.trim().length > 0; }","tryCatchPattern":"try {\n  await consumeCodexRateLimitResetCredit(accessToken, redeemRequestId);\n} catch (e) {\n  if (e.message.includes('redeem request id')) {\n    const credits = await getCodexRateLimitResetCredits(accessToken);\n    redeemRequestId = credits.credits[0]?.id; // recover by fetching a valid id\n    if (!redeemRequestId) throw new Error('No reset credits available.');\n    return consumeCodexRateLimitResetCredit(accessToken, redeemRequestId);\n  }\n  throw e;\n}","preventionTips":["Always fetch available credits first and use an id from that response","Validate request bodies (string, non-empty) before passing ids into service functions","Never construct redeem ids client-side; they come from the credits API","No credit is spent when this guard fires — safe to correct inputs and retry"],"tags":["validation","codex","missing-parameter"],"backgroundTag":"missing-required-parameter","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}