{"record":{"id":"595c7662bc592a22","repo":"koala73/worldmonitor","slug":"redis-returned-an-invalid-stored-value","errorCode":null,"errorMessage":"Redis returned an invalid stored value","messagePattern":"Redis returned an invalid stored value","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"api/oauth/_refresh-recovery.ts","lineNumber":217,"sourceCode":"    headers: {\n      Authorization: `Bearer ${token}`,\n      'Content-Type': 'application/json',\n      'User-Agent': 'worldmonitor-edge/1.0',\n    },\n    body: JSON.stringify(['EVAL', script, String(keys.length), ...keys, ...args]),\n    signal: AbortSignal.timeout(3_000),\n  });\n  if (!resp.ok) throw new Error(`Redis HTTP ${resp.status}`);\n  const data = (await resp.json().catch(() => null)) as { result?: unknown; error?: string } | null;\n  if (data?.error) throw new Error(`Redis EVAL failed: ${data.error}`);\n  if (!data || !Object.prototype.hasOwnProperty.call(data, 'result')) {\n    throw new Error('Redis EVAL returned an invalid response');\n  }\n  return data.result;\n}\n\nfunction parseStoredJson(value: unknown): unknown {\n  if (typeof value !== 'string') throw new Error('Redis returned an invalid stored value');\n  try {\n    return JSON.parse(value);\n  } catch {\n    throw new Error('Redis returned malformed JSON');\n  }\n}\n\nexport async function rawRedisBeginRefreshAttempt(\n  refreshToken: string,\n  attemptId: string,\n): Promise<RefreshConsumeResult> {\n  const refreshKey = `oauth:refresh:${refreshToken}`;\n  const attemptKey = refreshFamilyAttemptKey(refreshToken);\n  const pointerKey = refreshFamilyPointerKey(refreshToken);\n  const attemptValue = JSON.stringify({ attempt_id: attemptId });\n  const attemptMarker = serializeRefreshAttemptMarker(attemptValue);\n  const script = [\n    \"local value = redis.call('GET', KEYS[1])\",","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/koala73/worldmonitor/blob/a96956387a927b8cd7aa34b0c41fca357e746be9/api/oauth/_refresh-recovery.ts#L199-L235","documentation":"Thrown by `parseStoredJson` in api/oauth/_refresh-recovery.ts:217 during `rawRedisBeginRefreshAttempt` when the Lua script consumed the refresh token but the stored value under `oauth:refresh:<token>` (returned as `result[1]`) is not a string. The script only returns stored strings from GET, so a non-string means an unexpected RESP type — e.g. false/null leaked through or a key holding a non-string type.","triggerScenarios":"The consumed `oauth:refresh:<token>` value being absent, boolean false, or a non-string Redis type when the script path returns `{1, value}` with a non-string `value` (e.g. after manual console edits or a format migration gone wrong).","commonSituations":"Manual Redis console edits writing numbers/other types; a partially applied key-format migration; cross-environment key collisions between dev and prod sharing a database.","solutions":["Inspect `TYPE oauth:refresh:<token>` in Redis; it must be a string holding JSON","Delete or fix the malformed key so the next refresh attempt can repopulate it","Audit for seed scripts or consoles writing these keys outside the Edge code path"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Before parsing a stored value, confirm it is the expected string type\nconst raw = await redisGet(`oauth:refresh:${token}`);\nif (typeof raw !== 'string') {\n  // delete the malformed key / require re-login instead of crashing\n}","typeGuard":"function isStoredString(v: unknown): v is string {\n  return typeof v === 'string';\n}","tryCatchPattern":"try {\n  await beginRefreshAttempt(token, id);\n} catch (e) {\n  if (e instanceof Error && e.message === 'Redis returned an invalid stored value') {\n    // quarantine/delete the key; user must re-authenticate\n  } else throw e;\n}","preventionTips":["Keep all writes to oauth:refresh:* as JSON strings via the standard code path","Periodically audit TYPE of oauth keys to catch drift from manual edits"],"tags":["redis","data-corruption","oauth","refresh-token","parsing"],"backgroundTag":"corrupted-stored-value","analyzedSha":"a96956387a927b8cd7aa34b0c41fca357e746be9","analyzedAt":"2026-08-27T19:53:08.521Z","contentChangedAt":"2026-08-27T19:53:08.521Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}