{"record":{"id":"38ea1b2de2da84b3","repo":"koala73/worldmonitor","slug":"redis-transaction-failed-results-error-invalid-response","errorCode":null,"errorMessage":"Redis transaction failed: ${results?.error || 'invalid response'}","messagePattern":"Redis transaction failed: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/seed-portwatch-port-activity.mjs","lineNumber":1016,"sourceCode":"  // The canonical list and seed-meta are the publication pointers, so they\n  // must commit in the same transaction as the per-country state they name.\n  const resp = await fetchFn(`${credentials.url}/multi-exec`, {\n    method: 'POST',\n    headers: {\n      Authorization: `Bearer ${credentials.token}`,\n      'Content-Type': 'application/json',\n      'User-Agent': CHROME_UA,\n    },\n    body: JSON.stringify(commands),\n    signal: AbortSignal.timeout(30_000),\n  });\n  if (!resp.ok) {\n    const text = await resp.text().catch(() => '');\n    throw new Error(`Redis transaction failed: HTTP ${resp.status} — ${text.slice(0, 200)}`);\n  }\n  const results = await resp.json();\n  if (!Array.isArray(results) || results.length !== commands.length) {\n    throw new Error(`Redis transaction failed: ${results?.error || 'invalid response'}`);\n  }\n  const failures = results.filter((result) => result?.error || result?.result === 'ERR');\n  if (failures.length > 0) {\n    throw new Error(`Redis transaction: ${failures.length}/${commands.length} commands failed`);\n  }\n  return results;\n}\n\nconst CORRUPT_COUNTRY_CACHE = Symbol('corrupt country cache');\n\n// MGET-style batch read via the Upstash REST /pipeline endpoint. Returns an\n// array aligned with `keys` where each element is either the parsed JSON\n// payload, explicit miss, or confirmed corrupt value. Transport/envelope errors\n// remain fatal: only a validated upstream replacement may overwrite corruption.\n// Primes the per-country cache lookup in one round-trip instead of 174 GETs.\nasync function redisMgetJson(keys) {\n  if (keys.length === 0) return [];\n  const commands = keys.map((k) => ['GET', k]);","sourceCodeStart":998,"sourceCodeEnd":1034,"githubUrl":"https://github.com/koala73/worldmonitor/blob/7d06c8633d256c18e38133030bc3613976a96ec9/scripts/seed-portwatch-port-activity.mjs#L998-L1034","documentation":"Thrown by the same Upstash REST pipeline helper when the HTTP call succeeds (2xx) but the JSON body is not a well-formed transaction result: it is not an array, or its length does not equal the number of commands sent. This indicates Upstash accepted the request but returned an error object or unexpected payload (e.g. {error: ...} for an invalid command or wrong REST format).","triggerScenarios":"Sending a command list Upstash rejects wholesale (e.g. a command not permitted by the REST API, malformed JSON in a serialized value), hitting an endpoint that is not the Upstash pipeline/publish URL, or the REST endpoint returning an error object {error:'...'} with HTTP 200 via a proxy.","commonSituations":"Pointing UPSTASH_REDIS_REST_URL at the wrong path or a non-Upstash service; using a command unsupported by Upstash REST; oversized request body silently rejected; Upstash API version change altering the response envelope.","solutions":["Print results (the parsed body) to see the embedded error field returned by Upstash","Confirm the URL targets the Upstash REST pipeline endpoint (/pipeline) and the payload is an array of [command, ...args] arrays","Check every command's arguments are JSON-serializable strings/numbers (no undefined or nested objects where strings are expected)","Remove or replace any unsupported Redis commands for the REST API","Pin/verify Upstash API behavior against current docs if the envelope changed"],"exampleFix":"// before\nconst results = await resp.json();\nif (!Array.isArray(results) || results.length !== commands.length) {\n  throw new Error(`Redis transaction failed: ${results?.error || 'invalid response'}`);\n}\n// after\nconst results = await resp.json().catch(() => null);\nif (!Array.isArray(results) || results.length !== commands.length) {\n  throw new Error(`Redis transaction failed: ${results?.error || `unexpected body: ${JSON.stringify(results).slice(0, 200)}`}`);\n}","handlingStrategy":"type-guard","validationCode":"function validateUpstashPipelineBody(body, expectedCount) {\n  return Array.isArray(body) && body.length === expectedCount && body.every((r) => r === null || typeof r === 'object');\n}","typeGuard":"const isPipelineResultArray = (v, n) => Array.isArray(v) && v.length === n && v.every((r) => typeof r === 'object' || r === null);","tryCatchPattern":"try {\n  const results = await redisPipeline(commands);\n} catch (err) {\n  const bodyErr = err?.message.match(/Redis transaction failed: (.+)$/);\n  console.error('Upstash pipeline rejected request:', bodyErr?.[1] ?? err);\n  throw err;\n}","preventionTips":["Always POST [cmd, ...args] arrays to the /pipeline endpoint","Ensure every command argument is a JSON-serializable string or number","Log the parsed body whenever the result shape check fails to surface Upstash's error field","Test pipeline payloads against Upstash after any command-list change"],"tags":["redis","upstash","api","validation","seeding"],"backgroundTag":"unexpected-response-shape","analyzedSha":"7d06c8633d256c18e38133030bc3613976a96ec9","analyzedAt":"2026-09-15T16:44:39.439Z","contentChangedAt":"2026-09-15T16:44:39.439Z","schemaVersion":2},"datasetVersion":"2026-09-15T18:17:12.389Z"}