{"record":{"id":"4d5177096d8dab9b","repo":"redis/node-redis","slug":"action-id-actionid-failed-error-action-err","errorCode":null,"errorMessage":"Action id: ${actionId} failed! Error: ${action.error}","messagePattern":"Action id: (.+?) failed! Error: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/test-utils/lib/fault-injector/fault-injector-client.ts","lineNumber":117,"sourceCode":"    actionId: string,\n    {\n      timeoutMs,\n      maxWaitTimeMs,\n    }: {\n      timeoutMs?: number;\n      maxWaitTimeMs?: number;\n    } = {}\n  ): Promise<ActionStatus> {\n    const timeout = timeoutMs || 1000;\n    const maxWaitTime = maxWaitTimeMs || 60000;\n\n    const startTime = Date.now();\n\n    while (Date.now() - startTime < maxWaitTime) {\n      const action = await this.getActionStatus<ActionStatus>(actionId);\n\n      if (action.status === \"failed\") {\n        throw new Error(\n          `Action id: ${actionId} failed! Error: ${action.error}`\n        );\n      }\n\n      if ([\"finished\", \"success\"].includes(action.status)) {\n        dbg(`${actionId} completed: ${action.status}`)\n        return action;\n      }\n\n      await setTimeout(timeout);\n    }\n\n    throw new Error(`Timeout waiting for action ${actionId}`);\n  }\n\n  async migrateAndBindAction({\n    bdbId,\n    clusterIndex,","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/test-utils/lib/fault-injector/fault-injector-client.ts#L99-L135","documentation":"waitForAction polls getActionStatus for an action_id. If the fault-injector service reports status === 'failed', it throws immediately, embedding the server-side action.error. This fires before the maxWait timeout check, so a failed action short-circuits the wait.","triggerScenarios":"triggerAction(...) or migrateAndBindAction(...) where the fault-injector backend executes the action and it fails server-side (e.g., migrate target unreachable, database creation rejected, cluster index invalid).","commonSituations":"Requesting a migration to a cluster index that does not exist; bdb_id mismatch; create_database with invalid config; backend resource exhaustion; permissions on the target cluster.","solutions":["Read action.error in the message — it is the server's reason for the failure","Call getActionStatus(actionId) again or listActions to inspect the full action record","Validate the parameters (cluster_index, bdb_id, database_config) against the cluster's current state before retriggering"],"exampleFix":"// before\nawait fi.triggerAction({ type: 'migrate', parameters: { cluster_index: 9 } });\n// Error: Action id: abc failed! Error: cluster index 9 out of range\n\n// after — use a valid cluster index\nconst clusters = await fi.listActions();\nawait fi.triggerAction({ type: 'migrate', parameters: { cluster_index: 0 } });","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isFailedAction(a: ActionStatus): boolean {\n  return a.status === 'failed';\n}","tryCatchPattern":"try {\n  await fi.triggerAction(action);\n} catch (e) {\n  if (e instanceof Error && /Action id:.*failed!/.test(e.message)) {\n    const reason = e.message;\n  }\n  throw e;\n}","preventionTips":["Validate action parameters (cluster_index, bdb_id) against current cluster state before triggering","Log action_id on submission so a failure can be correlated with getActionStatus","Inspect action.error in the message to find the server-side root cause"],"tags":["fault-injection","http","test-utils","action-status"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}