{"record":{"id":"8c921dd090ecdb0c","repo":"koala73/worldmonitor","slug":"redis-http-resp-status-8c921d","errorCode":null,"errorMessage":"Redis HTTP ${resp.status}","messagePattern":"Redis HTTP (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/_shared/redis.ts","lineNumber":192,"sourceCode":"export async function getLargeRawJson(key: string, timeoutMs?: number): Promise<unknown | null> {\n  if (process.env.LOCAL_API_MODE === 'tauri-sidecar') {\n    const { sidecarCacheGet } = await import('./sidecar-cache');\n    return sidecarCacheGet(key);\n  }\n  const url = process.env.UPSTASH_REDIS_REST_URL;\n  const token = process.env.UPSTASH_REDIS_REST_TOKEN;\n  if (!url || !token) return null;\n  const resp = await fetch(`${url}/`, {\n    method: 'POST',\n    headers: {\n      Authorization: `Bearer ${token}`,\n      'Content-Type': 'application/json',\n      'User-Agent': 'worldmonitor-server/1.0 (redis)',\n    },\n    body: JSON.stringify(['GET', key]),\n    signal: AbortSignal.timeout(resolvePipelineTimeoutMs(timeoutMs)),\n  });\n  if (!resp.ok) throw new Error(`Redis HTTP ${resp.status}`);\n  const data = (await resp.json()) as { result?: string | null; error?: string };\n  if (data.error) throw new Error(`Redis command error: ${data.error}`);\n  if (!data.result) return null;\n  return unwrapEnvelope(JSON.parse(data.result)).data;\n}\n\n/**\n * Read a key's value as a raw Upstash string — no JSON.parse, no envelope unwrap.\n * Use when a seeder stores a bare scalar (e.g., a snapshot_id pointer) via\n * `['SET', key, bareString]` without JSON.stringify. getCachedJson() on these\n * keys silently returns null because JSON.parse throws on unquoted strings,\n * and the try/catch swallows the error.\n *\n * Always uses the raw (unprefixed) key — matches the seed-script write path\n * (seeders don't know about the Vercel env-prefix scheme).\n */\nexport async function getCachedRawString(key: string): Promise<string | null> {\n  if (process.env.LOCAL_API_MODE === 'tauri-sidecar') {","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/koala73/worldmonitor/blob/9361220cc013571781071f0206e4d80fd14b2f7f/server/_shared/redis.ts#L174-L210","documentation":"getLargeRawJson performs a GET against the Upstash Redis HTTP API and throws a plain Error 'Redis HTTP <status>' when the HTTP response is not ok. This signals a transport/API-level failure (auth, rate limit, gateway error) rather than a missing key — a null result is returned separately for that. readCanonicalFallback callers see this as a hard read failure and should treat Redis as unavailable.","triggerScenarios":"The fetch to the Upstash REST endpoint returns a non-2xx status: 401/403 from bad or rotated UPSTASH credentials, 429 from exceeding Upstash request quota, 5xx from Upstash or an intermediate proxy, or a network-layer error surfaced as a bad status.","commonSituations":"Missing or stale UPSTASH_REST_URL/UPSTASH_REST_TOKEN env vars in a fresh deploy; Upstash free-tier daily command limit exhausted; Upstash incident causing 502/503; wrong region URL after migrating the database.","solutions":["Check the status code in the message: 401/403 means fix UPSTASH credentials, 429 means quota/rate limit, 5xx means retry later or check Upstash status","Verify UPSTASH_REST_URL and UPSTASH_REST_TOKEN env vars are set correctly in the deployment","Check the Upstash console for quota exhaustion or plan limits and upgrade/backoff as needed","Retry with backoff; callers like readCanonicalFallback should fall back to the provider source when Redis is unavailable"],"exampleFix":"// before\nconst v = await getLargeRawJson(key); // throws on HTTP failure\n// after\nlet v = null;\ntry { v = await getLargeRawJson(key); }\ncatch (e) { console.warn('redis read failed, falling back', e); }","handlingStrategy":"try-catch","validationCode":"const redisConfigured = Boolean(process.env.UPSTASH_REST_URL && process.env.UPSTASH_REST_TOKEN);\nif (!redisConfigured) skipRedisAndUseFallback();","typeGuard":"function isRedisHttpError(e: unknown): e is Error & { redis: true } {\n  return e instanceof Error && /^Redis HTTP \\d+$/.test(e.message);\n}","tryCatchPattern":"try {\n  data = await getLargeRawJson(key);\n} catch (e) {\n  log.warn('redis GET failed', e);\n  data = null; // fall back to provider/source read\n}","preventionTips":["Verify UPSTASH_REST_URL/TOKEN in every environment before deploy","Monitor Upstash quota and set alerts before limits are hit","Wrap Redis reads with timeout + fallback so outages degrade gracefully","Retry 5xx/429 with exponential backoff instead of failing immediately"],"tags":["redis","http","upstash","network"],"backgroundTag":"redis-http-error","analyzedSha":"9361220cc013571781071f0206e4d80fd14b2f7f","analyzedAt":"2026-09-01T10:32:37.851Z","contentChangedAt":"2026-09-01T10:32:37.851Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}