{"record":{"id":"a8b5272fcc3c35d9","repo":"redis/node-redis","slug":"himport-prepare-discard-discardall-are-not-support","errorCode":null,"errorMessage":"HIMPORT PREPARE/DISCARD/DISCARDALL are not supported inside MULTI/pipeline; call them on the client before the transaction","messagePattern":"HIMPORT PREPARE/DISCARD/DISCARDALL are not supported inside MULTI/pipeline; call them on the client before the transaction","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/client/index.ts","lineNumber":52,"sourceCode":"import { ASKING_CMD } from '../commands/ASKING';\n\nconst noop = () => {};\n\nconst HIMPORT_SESSION_SUBCOMMANDS = new Set(['PREPARE', 'DISCARD', 'DISCARDALL']);\n\n/**\n * MULTI/pipeline stores raw args only, so the HIMPORT transparency hook never sees these\n * commands — a PREPARE/DISCARD executed that way would silently diverge the client registry\n * from server state (a fieldset the registry doesn't know about, or a discarded one it\n * would resurrect via lazy prepare). Rejected client-side at the exec funnel, which covers\n * both the typed multi methods and raw `multi.addCommand(...)`. HIMPORT SET stays allowed:\n * it mutates no registry state (the fieldset must already exist on the carrying connection).\n */\nfunction assertNoHimportSessionCommands(commands: Array<RedisMultiQueuedCommand>) {\n  for (const { args } of commands) {\n    if (String(args[0]).toUpperCase() !== 'HIMPORT') continue;\n    if (HIMPORT_SESSION_SUBCOMMANDS.has(String(args[1]).toUpperCase())) {\n      throw new Error(\n        'HIMPORT PREPARE/DISCARD/DISCARDALL are not supported inside MULTI/pipeline; call them on the client before the transaction'\n      );\n    }\n  }\n}\n\nexport interface RedisClientOptions<\n  M extends RedisModules = RedisModules,\n  F extends RedisFunctions = RedisFunctions,\n  S extends RedisScripts = RedisScripts,\n  RESP extends RespVersions = 3,\n  TYPE_MAPPING extends TypeMapping = TypeMapping,\n  SocketOptions extends RedisSocketOptions = RedisSocketOptions\n> extends CommanderConfig<M, F, S, RESP> {\n  /**\n   * `redis[s]://[[username][:password]@][host][:port][/db-number]`\n   * See [`redis`](https://www.iana.org/assignments/uri-schemes/prov/redis) and [`rediss`](https://www.iana.org/assignments/uri-schemes/prov/rediss) IANA registration for more details\n   */","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/client/lib/client/index.ts#L34-L70","documentation":"The client maintains a fieldset registry for HIMPORT transparency. MULTI/pipeline only stores raw args, so a HIMPORT PREPARE / DISCARD / DISCARDALL queued in a transaction would bypass the registry and silently diverge client state from server state. The client rejects these at the exec funnel. HIMPORT SET is allowed inside MULTI because it mutates no registry state.","triggerScenarios":"Queuing HIMPORT PREPARE/DISCARD/DISCARDALL via multi.addCommand(['HIMPORT','PREPARE',...]) and then exec(), or via typed multi methods, then calling exec().","commonSituations":"Trying to batch HIMPORT session setup inside a transaction for perceived atomicity; refactoring existing HIMPORT calls into a multi block without realising the registry implication.","solutions":["Issue HIMPORT PREPARE on the client directly (outside MULTI) before building the transaction.","Issue HIMPORT DISCARD/DISCARDALL on the client after the transaction completes.","Keep only HIMPORT SET inside the multi block."],"exampleFix":"// before\nconst m = client.multi();\nm.addCommand(['HIMPORT', 'PREPARE', fieldset]);\nm.hImportSet(fieldset, 'k', v);\nawait m.exec();\n\n// after\nawait client.hImportPrepare(fieldset);\nconst m = client.multi();\nm.hImportSet(fieldset, 'k', v);\nawait m.exec();","handlingStrategy":"validation","validationCode":"const HIMPORT_SESSION = new Set(['PREPARE', 'DISCARD', 'DISCARDALL']);\n\nfunction assertNoHimportInMulti(commands) {\n  for (const { args } of commands) {\n    if (String(args[0]).toUpperCase() === 'HIMPORT' && HIMPORT_SESSION.has(String(args[1]).toUpperCase())) {\n      throw new Error('HIMPORT PREPARE/DISCARD/DISCARDALL cannot be queued in MULTI; call them on the client directly');\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run HIMPORT PREPARE/DISCARD directly on the client; never inside multi().","Keep only HIMPORT SET inside transactions.","Document the registry-affecting subcommands so other contributors do not queue them."],"tags":["multi","pipeline","himport","transaction","validation"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}