{"record":{"id":"6825038bab0acf95","repo":"redis/node-redis","slug":"proxy-failed-to-send-smigrating-notification","errorCode":null,"errorMessage":"[Proxy] Failed to send SMIGRATING notification: ${migratingResult.error}","messagePattern":"\\[Proxy\\] Failed to send SMIGRATING notification: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/test-utils/lib/fault-injector/proxied-fault-injector-cluster.ts","lineNumber":107,"sourceCode":"      ? nodes.length - 1\n      : nodes.length;\n\n    const shouldMigrateHalfSlots = params.slot_migration === \"half\";\n    const slots = shouldMigrateHalfSlots\n      ? `0-${Math.floor(TOTAL_SLOTS / visibleNodesCount / 2) - 1}`\n      : `0-${Math.floor(TOTAL_SLOTS / visibleNodesCount) - 1}`;\n\n    const sMigratingNotification = buildSMigratingNotification(slots);\n\n    const sourceNode = nodes[0];\n\n    const migratingResult = await this.proxyController.sendToClient(\n      connections[sourceNode.id][0],\n      sMigratingNotification\n    );\n\n    if (!migratingResult.success) {\n      throw new Error(\n        `[Proxy] Failed to send SMIGRATING notification: ${migratingResult.error}`\n      );\n    }\n\n    // 2. Simulate maintenance delay\n    await setTimeout(2_000);\n\n    const isNewDestination = params.destination_type === \"new\";\n\n    if (isNewDestination && !nodeWithoutConnections) {\n      throw new Error(`[Proxy] No node with no connections`);\n    }\n\n    const destinationNode = isNewDestination\n      ? nodeWithoutConnections!\n      : nodes.at(-1)!; // Get the last node as the destination\n\n    if (!destinationNode) {","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/test-utils/lib/fault-injector/proxied-fault-injector-cluster.ts#L89-L125","documentation":"In triggerMigrate, after building the SMIGRATING push notification, the proxy controller attempts to send it to the source node's first client connection. If sendToClient returns success === false, this error throws with the controller's error detail. It means the proxy could not deliver the RESP push to the live client connection.","triggerScenarios":"The target client connection (connections[sourceNode.id][0]) was closed/reset between getConnections and sendToClient; the proxy controller's send buffer rejected the write; the connection id is stale.","commonSituations":"The Redis client disconnected mid-migration (timeout, network blip); the cluster client reconnected and connection ids changed; proxy controller in an inconsistent state after a prior failed action.","solutions":["Re-fetch connections immediately before sending: await proxyController.getConnections()","Ensure the cluster client stays connected during the migration (increase socket timeouts)","Inspect migratingResult.error in the message for the controller's specific refusal reason"],"exampleFix":"// before — stale connection id\nconst conn = connections[sourceNode.id][0];\nconst r = await this.proxyController.sendToClient(conn, msg);\n\n// after — refresh connections right before send\nconst fresh = await this.proxyController.getConnections();\nconst conn = fresh[sourceNode.id][0];\nconst r = await this.proxyController.sendToClient(conn, msg);","handlingStrategy":"retry","validationCode":"async function sendWithFreshConnection(proxyController: ProxyController, nodeId: string, msg: string) {\n  const fresh = await proxyController.getConnections();\n  const conn = fresh[nodeId]?.[0];\n  if (!conn) throw new Error(`No live connection for node ${nodeId}`);\n  return proxyController.sendToClient(conn, msg);\n}","typeGuard":"function isSendSuccess(r: { success: boolean; error?: string }): r is { success: true } {\n  return r.success === true;\n}","tryCatchPattern":"try {\n  const r = await proxyController.sendToClient(conn, msg);\n  if (!r.success) throw new Error(`[Proxy] Failed: ${r.error}`);\n} catch (e) {\n  if (e instanceof Error && /Failed to send SMIGRATING/.test(e.message)) {\n  }\n  throw e;\n}","preventionTips":["Re-fetch connections immediately before sendToClient to avoid stale ids","Keep the cluster client connected during migrations (raise socket timeouts)","Inspect migratingResult.error to distinguish connection-closed from proxy-internal failures"],"tags":["fault-injection","proxy","cluster","migration","test-utils"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}