{"record":{"id":"89764fcdc14744fd","repo":"windmill-labs/windmill","slug":"step-name-failed","errorCode":null,"errorMessage":"Step '${name}' failed","messagePattern":"Step '(.+?)' failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"typescript-client/client.ts","lineNumber":1912,"sourceCode":"    }\n\n    console.log(`\\n--- WAC: sleep(${key}, ${seconds}s) ---`);\n    this._raiseSuspend({\n      mode: \"sleep\",\n      key,\n      seconds: Math.max(1, Math.round(seconds)),\n      steps: [],\n    });\n  }\n\n  async _runInlineStep<T>(name: string, fn: () => T | Promise<T>): Promise<T> {\n    this._rethrowSwallowed();\n    const key = this._allocKey(name || \"step\");\n\n    if (key in this.completed) {\n      const value = this.completed[key];\n      if (value && typeof value === \"object\" && (value as any).__wmill_error) {\n        throw taskErrorFromMarker(value, `Step '${name}' failed`);\n      }\n      return value as T;\n    }\n\n    if (this._executingKey !== null) {\n      return new Promise(() => {});\n    }\n\n    console.log(`\\n--- WAC: ${key} ---`);\n    const startedAt = new Date().toISOString();\n    console.log(`WM_WAC_STEP: ${JSON.stringify({ key, started_at: startedAt })}`);\n    const t0 = Date.now();\n    // A thrown step still has to reach `completed_steps`, or a replay with\n    // `_executingKey` set finds nothing recorded and parks forever on the\n    // never-resolving promise above. A nested StepSuspend is control flow,\n    // not a step failure.\n    let result: T;\n    let errored = false;","sourceCodeStart":1894,"sourceCodeEnd":1930,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/typescript-client/client.ts#L1894-L1930","documentation":"In the Windmill TypeScript client, `ctx.step(name, fn)` checkpoints its result so replays of a `workflow()` run do not re-execute completed steps. If a step previously failed, the failure was stored as an error marker (`__wmill_error`) in the completed map; on replay, the context rebuilds and throws it as a `TaskError` with message \"Step '<name>' failed\". This is deliberate: a failed step stays failed on every round, and catch handlers get the same error shape each time.","triggerScenarios":"Calling `workflow()` ctx.step(name, fn) where the step's key already exists in the completed/checkpoint map with a value carrying `__wmill_error: true` — i.e. a replay round (recovery, resume, or later round of the re-run-from-top loop) reaching a step that threw in an earlier round.","commonSituations":"A workflow run resumes after a crash or suspension and re-enters the body from the top; the step failed in the first round and has no successful checkpoint. Also hit when a try/catch inside the workflow body catches a step failure and continues, so subsequent rounds replay the step and the stored failure is rethrown deterministically.","solutions":["Fix the root cause that made the step fail — inspect the thrown error's `.result` (serialized original error) and `.step_key` for details.","Wrap the step call in try/catch inside the workflow body and handle the failure (e.g. return a fallback or re-run with corrected inputs).","Change the step name/key if the old failed checkpoint must be bypassed deliberately (creates a fresh key).","Delete or fix the underlying resource the step operated on (bad credential, missing input, flaky downstream) and resume the run."],"exampleFix":"// before: unhandled failing step kills the workflow on every replay\nconst data = await ctx.step('fetch', () => fetchRecords());\n\n// after: handle failure explicitly so replay is consistent\nlet data;\ntry {\n  data = await ctx.step('fetch', () => fetchRecords());\n} catch (e) {\n  data = []; // fallback; same branch taken on every replay round\n}","handlingStrategy":"try-catch","validationCode":"// before calling step, check the checkpoint map (internal, debug use)\nconst key = name || 'step';\nif (key in ctx.completed && ctx.completed[key]?.__wmill_error) {\n  console.warn(`step '${key}' has a stored failure; it will rethrow on replay`);\n}","typeGuard":"function isWmillErrorMarker(v: unknown): v is { __wmill_error: true; message?: string; result?: { error: unknown }; step_key?: string } {\n  return typeof v === 'object' && v !== null && (v as any).__wmill_error === true;\n}","tryCatchPattern":"try {\n  const value = await ctx.step('myStep', fn);\n} catch (e: any) {\n  if (e?.name === 'TaskError') {\n    // inspect e.result.error for the original cause, e.step_key for the step\n  } else throw e;\n}","preventionTips":["Validate step inputs before the step body runs so transient failures don't get checkpointed.","Wrap unreliable I/O inside the step fn with bounded retries rather than letting it throw.","Remember the workflow body re-runs from the top each round — never assume a failed step is retried silently.","Keep step names stable and meaningful so checkpoint keys and failures map clearly to code."],"tags":["windmill","typescript","workflow","step-failed","replay","checkpoint"],"backgroundTag":"workflow-step-failed-replay","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}