{"record":{"id":"c44ec282eb6eb8e0","repo":"koala73/worldmonitor","slug":"redis-refresh-attempt-consume-returned-an-invalid","errorCode":null,"errorMessage":"Redis refresh-attempt consume returned an invalid response","messagePattern":"Redis refresh-attempt consume returned an invalid response","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"api/oauth/_refresh-recovery.ts","lineNumber":254,"sourceCode":"    'if value then',\n    '  local ok, decoded = pcall(cjson.decode, value)',\n    \"  if ok and type(decoded) == 'table' and decoded.kind == 'refresh_attempt' then\",\n    \"    return {0, redis.call('EXISTS', KEYS[2]), redis.call('GET', KEYS[3]) or false}\",\n    '  end',\n    \"  redis.call('SET', KEYS[2], ARGV[1], 'EX', ARGV[3])\",\n    \"  redis.call('SET', KEYS[1], ARGV[2], 'EX', ARGV[3])\",\n    '  return {1, value}',\n    'end',\n    \"return {0, redis.call('EXISTS', KEYS[2]), redis.call('GET', KEYS[3]) or false}\",\n  ].join('\\n');\n  const result = await rawRedisEval(\n    script,\n    [refreshKey, attemptKey, pointerKey],\n    [attemptValue, attemptMarker, REFRESH_ATTEMPT_TTL_SECONDS],\n  );\n\n  if (!Array.isArray(result) || (result[0] !== 0 && result[0] !== 1)) {\n    throw new Error('Redis refresh-attempt consume returned an invalid response');\n  }\n  if (result[0] === 1) {\n    return { kind: 'consumed', refreshData: parseStoredJson(result[1]), attemptValue };\n  }\n  return {\n    kind: 'miss',\n    recoveryPending: result[1] === 1,\n    familyId: result[2] ? familyIdFromRefreshPointer(parseStoredJson(result[2])) : null,\n  };\n}\n\nexport async function rawRedisRestoreRefreshAttempt(\n  refreshToken: string,\n  attemptValue: string,\n  refreshData: unknown,\n  familyId: string | null,\n): Promise<boolean> {\n  const script = [","sourceCodeStart":236,"sourceCodeEnd":272,"githubUrl":"https://github.com/koala73/worldmonitor/blob/a96956387a927b8cd7aa34b0c41fca357e746be9/api/oauth/_refresh-recovery.ts#L236-L272","documentation":"Thrown by `rawRedisBeginRefreshAttempt` in api/oauth/_refresh-recovery.ts:254 when the EVAL succeeded at the HTTP/Lua level but the returned payload is not the expected array `[0|1, ...]`. The Lua script always returns `{0, exists, pointer}` or `{1, value}`, so a non-array result or a leading element other than 0/1 means Upstash mangled the reply (e.g. RESP3 attributes, bulk-string coercion of the array, or an API change in reply encoding).","triggerScenarios":"An OAuth refresh attempt whose EVAL response comes back as a stringified array, a nested object, or `nil` — most plausibly from an Upstash REST reply-format change or an intermediate proxy re-encoding the JSON.","commonSituations":"Upstash API behavior/version changes; proxies or service workers mutating response bodies; severe payload corruption between Upstash and the Edge runtime.","solutions":["Capture and log the actual `result` payload from the EVAL response to see its real shape","Check Upstash status/changelogs for REST reply-format changes; pin or verify the endpoint and account settings","If the result is a stringified array, that indicates response re-encoding — remove the intermediary or report the format change; until fixed the refresh fails retryable-safe"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"function isInvalidConsumeResponse(e: unknown): boolean {\n  return e instanceof Error && e.message === 'Redis refresh-attempt consume returned an invalid response';\n}","tryCatchPattern":"try {\n  await beginRefreshAttempt(token, id);\n} catch (e) {\n  if (isInvalidConsumeResponse(e)) {\n    // reply was mangled: retry with backoff; recovery flow keeps the token safe\n    await retryWithBackoff();\n  } else throw e;\n}","preventionTips":["Capture and log the raw EVAL result when this fires to build evidence of reply-format changes","Avoid intermediaries that re-encode Upstash REST responses"],"tags":["redis","upstash","protocol","oauth","response-parsing"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"a96956387a927b8cd7aa34b0c41fca357e746be9","analyzedAt":"2026-08-27T19:53:08.521Z","schemaVersion":2},"datasetVersion":"2026-08-31T04:17:50.494Z"}