{"record":{"id":"9a4430042390f700","repo":"koala73/worldmonitor","slug":"err-message","errorCode":null,"errorMessage":"${err.message}","messagePattern":"\\$\\{err\\.message\\}","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"docker/redis-rest-proxy.mjs","lineNumber":952,"sourceCode":"      const multi = client.multi();\n      for (const cmd of commands) {\n        // 403 means one thing: the gate refused this command. It used to mean\n        // \"something threw somewhere in here\" — the try wrapped the queueing\n        // call too, so the TypeError below was reported to operators as an\n        // authorization failure, and #8265 read as a misconfigured REDIS_TOKEN\n        // for as long as it did.\n        //\n        // Narrowing the try is not enough on its own: commandForExecution\n        // itself throws a TypeError, not a gate rejection, when a body element\n        // is null or undefined (`String(args[0])`). Keying on the tag rather\n        // than on \"this line threw\" is what keeps a malformed body out of the\n        // 403 channel. Everything untagged falls through to respondError().\n        let queuedCommand;\n        try {\n          queuedCommand = commandForExecution(cmd);\n        } catch (err) {\n          if (!err?.commandNotAllowed) throw err;\n          res.writeHead(403);\n          res.end(JSON.stringify({ error: err.message }));\n          return;\n        }\n        // addCommand, not sendCommand: sendCommand is a method on the node-redis\n        // CLIENT, and the v4 transaction chain (redis@4, per\n        // docker/Dockerfile.redis-rest) queues raw commands with addCommand. The\n        // wrong one made every /multi-exec fail, so both seeders that publish\n        // exclusively through it — seed-fred-rates, seed-bis-extended — never\n        // wrote a key on a self-hosted install (#8265).\n        multi.addCommand(queuedCommand);\n      }\n      // exec() rejects two different ways, and only one of them is a response\n      // rather than a failure. Both were unreachable until #8265 was fixed —\n      // nothing was ever queued — so neither has run in production.\n      //\n      // MultiErrorReply means the transaction RAN and some commands replied\n      // with an error; node-redis hides the ordered replies on err.replies\n      // instead of returning them. Upstash answers that with HTTP 200 and an","sourceCodeStart":934,"sourceCodeEnd":970,"githubUrl":"https://github.com/koala73/worldmonitor/blob/e586b8b4b80f595aa7ece295eec10d76f2921240/docker/redis-rest-proxy.mjs#L934-L970","documentation":"When queueing a command for execution, the proxy catches errors from commandForExecution and re-throws any error lacking the commandNotAllowed flag, letting respondError turn it into a 500 with the error's message. So this message is the raw text of whatever non-authorization failure occurred while building/queueing the command (e.g. the malformed-command TypeError from assertCommandAllowed propagating, or a queue/serialization failure).","triggerScenarios":"Any exception thrown by commandForExecution that is not flagged commandNotAllowed: malformed command structures slipping past earlier checks, protocol/encoding problems in the queued command, or internal redis client errors during addCommand.","commonSituations":"Commands with wrong element types (nulls/objects as arguments) that pass the first-element check but fail later validation; client bugs in building multi-element commands; redis client version mismatches in the transaction chain.","solutions":["Read the wrapped err.message in the 500 response; it names the underlying cause to fix.","Validate command arguments are all JSON-encodable primitives before POSTing.","Check for null elements in the command array — these throw before the 403 channel.","Verify the node-redis version matches the transaction API the proxy expects (addCommand on the v4 chain)."],"exampleFix":"// before\nconst args = ['SET', 'k', null]; // null element -> 500\n// after\nconst args = ['SET', 'k', 'value'];","handlingStrategy":"try-catch","validationCode":"if (args.some((a) => a === null || a === undefined || typeof a === 'object')) throw new Error('command args must be JSON primitives');","typeGuard":"const isQueueable = (args) => Array.isArray(args) && args.every((a) => ['string','number','boolean'].includes(typeof a));","tryCatchPattern":"const res = await proxyCommand(args);\nif (res.status === 500) { const { error } = await res.json(); console.error('command queueing failed:', error); /* fix args and retry */ }","preventionTips":["Validate every command element is a primitive before sending","Avoid null/undefined elements in command arrays","Keep the node-redis major version pinned to what the proxy's transaction chain expects","Log the full failing command when a 500 comes back to speed diagnosis"],"tags":["redis","http","proxy","internal-error"],"backgroundTag":"http-error-response","analyzedSha":"e586b8b4b80f595aa7ece295eec10d76f2921240","analyzedAt":"2026-09-22T01:50:49.965Z","contentChangedAt":"2026-09-22T01:50:49.965Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}