{"id":"7bb34c8116902ac3","repo":"redis/node-redis","slug":"could-not-find-shard","errorCode":null,"errorMessage":"Could not find shard","messagePattern":"Could not find shard","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/cluster/cluster-slots.ts","lineNumber":422,"sourceCode":"            const promises: Promise<unknown>[] = [];\n            destMasterNode = this.#initiateSlotNode({ host: host, port: port, id: `smigrated-${host}:${port}` }, false, true, new Set(), promises);\n            await Promise.all([...promises, this.#initiateShardedPubSubClient(destMasterNode)]);\n            // Pause new destination until migration is complete\n            destMasterNode.client?._pause();\n            destMasterNode.pubSub?.client._pause();\n            // In case destination node didnt exist, this means Shard didnt exist as well, so creating a new Shard is completely fine\n            destShard = {\n              master: destMasterNode\n            };\n          } else {\n            // DEBUG: Log all master hosts/ports in slots array to diagnose mismatch\n            const allMasters = [...new Set(this.slots)].map(s => `${s.master.host}:${s.master.port}`);\n            dbgMaintenance(`[CSlots]: Searching for shard with host=${host}, port=${port}. Available masters in slots: ${allMasters.join(', ')}`);\n            // In case destination node existed, this means there was a Shard already, so its best if we can find it.\n            const existingShard = this.slots.find(shard => shard.master.host === host && shard.master.port === port);\n            if (!existingShard) {\n              dbgMaintenance(\"Could not find shard\");\n              throw new Error('Could not find shard');\n            }\n            destShard = existingShard;\n            // Pause existing destination during command transfer\n            destMasterNode.client?._pause();\n            destMasterNode.pubSub?.client._pause();\n          }\n\n          // Track last destination for slotless commands later\n          lastDestNode = destMasterNode;\n\n          // 3. Convert slots to Set and update shard mappings\n          const destinationSlots = new Set<number>();\n          for (const slot of slots) {\n            if (typeof slot === 'number') {\n              this.slots[slot] = destShard;\n              destinationSlots.add(slot);\n              allMovingSlots.add(slot);\n            } else {","sourceCodeStart":404,"sourceCodeEnd":440,"githubUrl":"https://github.com/redis/node-redis/blob/bb5beb56578573910e2ee8f39681edc214c41398/packages/client/lib/cluster/cluster-slots.ts#L404-L440","documentation":"Thrown inside #handleSmigrated (cluster-slots.ts:422) during Enterprise SMIGRATED slot-migration handling. When a destination node already exists in nodeByAddress but its host:port does not correspond to any Shard in this.slots, the migration code cannot determine which shard owns the destination and throws. This is an internal topology invariant violation, not a normal operational error.","triggerScenarios":"An Enterprise Redis cluster emits an SMIGRATED event whose destination address exists as a node but is not represented as a Shard in the slots array — typically a topology that is out of sync with the migration event (e.g. concurrent topology refresh, stale SMIGRATED seqId, or a race between discovery and migration).","commonSituations":"Active slot migrations under Enterprise maintenance while a topology refresh races; duplicate/delayed SMIGRATED events; version mismatch between client topology assumptions and server migration state.","solutions":["Let the next background topology refresh resync this.slots; the error is emitted on 'error' and the migration entry is skipped, so the cluster can self-heal.","Attach a cluster.on('error', ...) handler to observe and log these without crashing.","If recurring, trigger an explicit topology refresh and report the SMIGRATED sequence + addresses to maintainers — it indicates a client/server topology disagreement."],"exampleFix":"// no user API triggers this directly; handle defensively:\ncluster.on('error', (err) => {\n  if (err.message === 'Could not find shard') {\n    logger.warn('SMIGRATED topology mismatch, will resync on next refresh', err);\n    return; // do not crash\n  }\n  throw err;\n});","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isShardNotFound(e: unknown): boolean {\n  return e instanceof Error && /Could not find shard/i.test(e.message);\n}","tryCatchPattern":"cluster.on('error', (e) => {\n  if (isShardNotFound(e)) { logger.warn('topology mismatch during SMIGRATED; will resync', e); return; }\n  throw e;\n});","preventionTips":["Always attach a cluster.on('error') handler — this surfaces there.","Let background topology refresh self-heal rather than crashing.","Recurring occurrences warrant a maintainer report with seqId/addresses."],"tags":["cluster","migration","topology","enterprise","internal"],"analyzedSha":"bb5beb56578573910e2ee8f39681edc214c41398","analyzedAt":"2026-08-03T19:09:15.686Z","schemaVersion":2}