workflow 5.0.0-beta.5 → 5.0.0-beta.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -23
- package/dist/api-workflow.d.ts +1 -1
- package/dist/api-workflow.d.ts.map +1 -1
- package/dist/api-workflow.js +1 -1
- package/dist/api.d.ts +3 -3
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +5 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/internal/builtins.d.ts +20 -3
- package/dist/internal/builtins.d.ts.map +1 -1
- package/dist/internal/builtins.js +68 -4
- package/dist/internal/errors.d.ts +1 -1
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/errors.js +2 -2
- package/dist/nest-builder.d.ts +2 -0
- package/dist/nest-builder.d.ts.map +1 -0
- package/dist/nest-builder.js +2 -0
- package/dist/nest-vercel-builder.d.ts +2 -0
- package/dist/nest-vercel-builder.d.ts.map +1 -0
- package/dist/nest-vercel-builder.js +2 -0
- package/dist/observability.d.ts +1 -1
- package/dist/observability.js +2 -2
- package/dist/runtime.d.ts +2 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +4 -1
- package/docs/ai/chat-session-modeling.mdx +29 -26
- package/docs/ai/defining-tools.mdx +6 -7
- package/docs/ai/human-in-the-loop.mdx +11 -11
- package/docs/ai/index.mdx +50 -45
- package/docs/ai/message-queueing.mdx +16 -16
- package/docs/ai/meta.json +1 -0
- package/docs/ai/resumable-streams.mdx +40 -28
- package/docs/ai/sleep-and-delays.mdx +10 -10
- package/docs/ai/streaming-updates-from-tools.mdx +6 -6
- package/docs/api-reference/index.mdx +24 -0
- package/docs/api-reference/meta.json +8 -0
- package/docs/api-reference/vitest/index.mdx +9 -15
- package/docs/api-reference/workflow/create-hook.mdx +89 -10
- package/docs/api-reference/workflow/create-webhook.mdx +16 -15
- package/docs/api-reference/workflow/define-hook.mdx +35 -33
- package/docs/api-reference/workflow/fatal-error.mdx +30 -8
- package/docs/api-reference/workflow/fetch.mdx +14 -10
- package/docs/api-reference/workflow/get-step-metadata.mdx +2 -2
- package/docs/api-reference/workflow/get-workflow-metadata.mdx +3 -3
- package/docs/api-reference/workflow/get-writable.mdx +7 -7
- package/docs/api-reference/workflow/index.mdx +4 -1
- package/docs/api-reference/workflow/retryable-error.mdx +1 -1
- package/docs/api-reference/workflow/set-attributes.mdx +61 -0
- package/docs/api-reference/workflow/sleep.mdx +4 -4
- package/docs/api-reference/workflow-ai/durable-agent.mdx +48 -86
- package/docs/api-reference/workflow-ai/index.mdx +3 -3
- package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +67 -24
- package/docs/api-reference/workflow-api/get-hook-by-token.mdx +26 -12
- package/docs/api-reference/workflow-api/get-run.mdx +43 -8
- package/docs/api-reference/workflow-api/index.mdx +6 -10
- package/docs/api-reference/workflow-api/resume-hook.mdx +73 -12
- package/docs/api-reference/workflow-api/resume-webhook.mdx +11 -9
- package/docs/api-reference/workflow-api/start.mdx +60 -13
- package/docs/api-reference/workflow-astro/index.mdx +18 -0
- package/docs/api-reference/workflow-astro/meta.json +4 -0
- package/docs/api-reference/workflow-astro/workflow.mdx +45 -0
- package/docs/api-reference/workflow-errors/entity-conflict-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/hook-conflict-error.mdx +60 -0
- package/docs/api-reference/workflow-errors/hook-not-found-error.mdx +8 -8
- package/docs/api-reference/workflow-errors/index.mdx +88 -0
- package/docs/api-reference/workflow-errors/meta.json +6 -0
- package/docs/api-reference/workflow-errors/precondition-failed-error.mdx +68 -0
- package/docs/api-reference/workflow-errors/run-expired-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/run-not-supported-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/step-not-registered-error.mdx +5 -5
- package/docs/api-reference/workflow-errors/throttle-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/too-early-error.mdx +2 -2
- package/docs/api-reference/workflow-errors/workflow-error.mdx +52 -0
- package/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx +5 -6
- package/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx +6 -6
- package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +5 -5
- package/docs/api-reference/workflow-errors/workflow-run-not-completed-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx +4 -4
- package/docs/api-reference/workflow-errors/workflow-runtime-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/workflow-world-error.mdx +8 -8
- package/docs/api-reference/workflow-globals.mdx +14 -10
- package/docs/api-reference/workflow-nest/configure-workflow-controller.mdx +33 -0
- package/docs/api-reference/workflow-nest/index.mdx +31 -0
- package/docs/api-reference/workflow-nest/meta.json +9 -0
- package/docs/api-reference/workflow-nest/nest-local-builder.mdx +64 -0
- package/docs/api-reference/workflow-nest/workflow-controller.mdx +40 -0
- package/docs/api-reference/workflow-nest/workflow-module.mdx +74 -0
- package/docs/api-reference/workflow-next/with-workflow.mdx +39 -17
- package/docs/api-reference/workflow-nitro/index.mdx +60 -0
- package/docs/api-reference/workflow-nuxt/index.mdx +48 -0
- package/docs/api-reference/workflow-observability/hydrate-data.mdx +35 -0
- package/docs/api-reference/workflow-observability/hydrate-resource-io.mdx +62 -0
- package/docs/api-reference/workflow-observability/index.mdx +62 -0
- package/docs/api-reference/workflow-observability/meta.json +11 -0
- package/docs/api-reference/workflow-observability/observability-revivers.mdx +50 -0
- package/docs/api-reference/workflow-observability/parse-class-name.mdx +41 -0
- package/docs/api-reference/workflow-observability/parse-step-name.mdx +40 -0
- package/docs/api-reference/workflow-observability/parse-workflow-name.mdx +55 -0
- package/docs/api-reference/workflow-runtime/create-world.mdx +39 -0
- package/docs/api-reference/workflow-runtime/get-world-handlers.mdx +44 -0
- package/docs/api-reference/{workflow-api → workflow-runtime}/get-world.mdx +11 -14
- package/docs/api-reference/workflow-runtime/health-check.mdx +50 -0
- package/docs/api-reference/workflow-runtime/index.mdx +41 -0
- package/docs/api-reference/workflow-runtime/meta.json +12 -0
- package/docs/api-reference/workflow-runtime/set-world.mdx +51 -0
- package/docs/api-reference/workflow-runtime/workflow-entrypoint.mdx +43 -0
- package/docs/api-reference/workflow-runtime/world/analytics.mdx +315 -0
- package/docs/api-reference/workflow-runtime/world/index.mdx +60 -0
- package/docs/api-reference/workflow-runtime/world/meta.json +4 -0
- package/docs/api-reference/workflow-runtime/world/queue.mdx +88 -0
- package/docs/api-reference/{workflow-api → workflow-runtime}/world/storage.mdx +98 -34
- package/docs/api-reference/{workflow-api → workflow-runtime}/world/streams.mdx +8 -8
- package/docs/api-reference/workflow-serde/index.mdx +1 -2
- package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +3 -4
- package/docs/api-reference/workflow-serde/workflow-serialize.mdx +8 -8
- package/docs/api-reference/workflow-sveltekit/index.mdx +18 -0
- package/docs/api-reference/workflow-sveltekit/meta.json +4 -0
- package/docs/api-reference/workflow-sveltekit/workflow-plugin.mdx +42 -0
- package/docs/api-reference/workflow-vite/index.mdx +18 -0
- package/docs/api-reference/workflow-vite/meta.json +4 -0
- package/docs/api-reference/workflow-vite/workflow.mdx +48 -0
- package/docs/changelog/attributes-mvp.mdx +380 -0
- package/docs/changelog/batched-event-writes.mdx +79 -0
- package/docs/changelog/eager-processing.mdx +110 -436
- package/docs/changelog/index.mdx +4 -2
- package/docs/changelog/lazy-event-creation.md +127 -0
- package/docs/changelog/lazy-hook-resume.mdx +78 -0
- package/docs/changelog/meta.json +11 -1
- package/docs/changelog/resilient-resume.mdx +32 -0
- package/docs/changelog/resilient-start.mdx +33 -285
- package/docs/changelog/step-message-ownership.mdx +360 -0
- package/docs/changelog/turbo-mode.md +87 -0
- package/docs/comparisons/index.mdx +66 -0
- package/docs/comparisons/meta.json +11 -0
- package/docs/comparisons/workflow-sdk-vs-aws-agentcore.mdx +55 -0
- package/docs/comparisons/workflow-sdk-vs-aws-step-functions.mdx +111 -0
- package/docs/comparisons/workflow-sdk-vs-cloudflare-workflows.mdx +71 -0
- package/docs/comparisons/workflow-sdk-vs-inngest.mdx +102 -0
- package/docs/comparisons/workflow-sdk-vs-temporal.mdx +123 -0
- package/docs/comparisons/workflow-sdk-vs-trigger-dev.mdx +104 -0
- package/docs/configuration/build-and-diagnostics.mdx +70 -0
- package/docs/configuration/cli-and-web-ui.mdx +241 -0
- package/docs/configuration/framework-options.mdx +165 -0
- package/docs/configuration/index.mdx +32 -0
- package/docs/configuration/meta.json +12 -0
- package/docs/configuration/runtime-tuning.mdx +376 -0
- package/docs/configuration/worlds.mdx +313 -0
- package/docs/cookbook/advanced/child-workflows.mdx +211 -264
- package/docs/cookbook/advanced/meta.json +6 -1
- package/docs/cookbook/advanced/publishing-libraries.mdx +65 -56
- package/docs/cookbook/advanced/serializable-steps.mdx +28 -20
- package/docs/cookbook/advanced/upgrading-workflows.mdx +199 -0
- package/docs/cookbook/agent-patterns/agent-cancellation.mdx +27 -19
- package/docs/cookbook/agent-patterns/durable-agent.mdx +14 -142
- package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +30 -22
- package/docs/cookbook/common-patterns/batching.mdx +18 -14
- package/docs/cookbook/common-patterns/idempotency.mdx +41 -53
- package/docs/cookbook/common-patterns/rate-limiting.mdx +8 -4
- package/docs/cookbook/common-patterns/saga.mdx +23 -19
- package/docs/cookbook/common-patterns/scheduling.mdx +34 -22
- package/docs/cookbook/common-patterns/sequential-and-parallel.mdx +29 -25
- package/docs/cookbook/common-patterns/timeouts.mdx +26 -21
- package/docs/cookbook/common-patterns/webhooks.mdx +10 -6
- package/docs/cookbook/common-patterns/workflow-composition.mdx +30 -27
- package/docs/cookbook/index.mdx +22 -21
- package/docs/cookbook/integrations/ai-sdk.mdx +85 -47
- package/docs/cookbook/integrations/chat-sdk.mdx +50 -33
- package/docs/cookbook/integrations/sandbox.mdx +62 -45
- package/docs/deploying.mdx +95 -0
- package/docs/errors/abort-signal-timeout-in-workflow.mdx +16 -12
- package/docs/errors/corrupted-event-log.mdx +29 -18
- package/docs/errors/deployment-mismatch.mdx +71 -0
- package/docs/errors/fetch-in-workflow.mdx +11 -7
- package/docs/errors/hook-conflict.mdx +69 -13
- package/docs/errors/index.mdx +2 -36
- package/docs/errors/node-js-module-in-workflow.mdx +9 -5
- package/docs/errors/replay-divergence.mdx +27 -0
- package/docs/errors/run-expired.mdx +85 -0
- package/docs/errors/runtime-decryption-failed.mdx +77 -0
- package/docs/errors/serialization-failed.mdx +44 -12
- package/docs/errors/start-invalid-workflow-function.mdx +9 -5
- package/docs/errors/step-executed-multiple-times.mdx +23 -0
- package/docs/errors/step-not-registered.mdx +6 -6
- package/docs/errors/timeout-in-workflow.mdx +12 -8
- package/docs/errors/webhook-invalid-respond-with-value.mdx +18 -18
- package/docs/errors/webhook-response-not-sent.mdx +20 -16
- package/docs/errors/workflow-not-registered.mdx +5 -5
- package/docs/foundations/cancellation.mdx +31 -32
- package/docs/foundations/errors-and-retries.mdx +42 -11
- package/docs/foundations/hooks.mdx +64 -35
- package/docs/foundations/idempotency.mdx +244 -12
- package/docs/foundations/index.mdx +1 -23
- package/docs/foundations/meta.json +2 -1
- package/docs/foundations/serialization.mdx +21 -22
- package/docs/foundations/starting-workflows.mdx +106 -30
- package/docs/foundations/streaming.mdx +107 -59
- package/docs/foundations/versioning.mdx +263 -0
- package/docs/foundations/workflows-and-steps.mdx +9 -9
- package/docs/getting-started/astro.mdx +22 -18
- package/docs/getting-started/express.mdx +15 -11
- package/docs/getting-started/fastify.mdx +15 -11
- package/docs/getting-started/hono.mdx +15 -11
- package/docs/getting-started/index.mdx +10 -3
- package/docs/getting-started/meta.json +3 -1
- package/docs/getting-started/nestjs.mdx +87 -20
- package/docs/getting-started/next.mdx +22 -16
- package/docs/getting-started/nitro.mdx +22 -18
- package/docs/getting-started/nuxt.mdx +15 -11
- package/docs/getting-started/python.mdx +135 -40
- package/docs/getting-started/react-router/index.mdx +33 -0
- package/docs/getting-started/react-router/meta.json +5 -0
- package/docs/getting-started/react-router/v7.mdx +237 -0
- package/docs/getting-started/react-router/v8.mdx +232 -0
- package/docs/getting-started/sveltekit.mdx +20 -16
- package/docs/getting-started/tanstack-start.mdx +17 -13
- package/docs/getting-started/vite.mdx +15 -11
- package/docs/how-it-works/cancellation.mdx +63 -63
- package/docs/how-it-works/code-transform.mdx +83 -67
- package/docs/how-it-works/encryption.mdx +30 -26
- package/docs/how-it-works/event-sourcing.mdx +98 -34
- package/docs/how-it-works/framework-integrations.mdx +96 -337
- package/docs/how-it-works/understanding-directives.mdx +22 -22
- package/docs/internal/index.mdx +6 -4
- package/docs/internal/meta.json +6 -1
- package/docs/internal/nitro-native-build.mdx +38 -0
- package/docs/internal/nitro-web-ui.mdx +24 -0
- package/docs/internal/serializable-abort-controller.mdx +7 -7
- package/docs/meta.json +3 -2
- package/docs/observability/attributes.mdx +134 -0
- package/docs/observability/index.mdx +32 -10
- package/docs/observability/meta.json +1 -1
- package/docs/observability/retention.mdx +93 -0
- package/docs/observability/tracing.mdx +124 -0
- package/docs/testing/index.mdx +36 -36
- package/docs/testing/server-based.mdx +10 -10
- package/docs/whats-new.mdx +186 -0
- package/package.json +17 -14
- package/docs/api-reference/workflow-api/world/index.mdx +0 -58
- package/docs/api-reference/workflow-api/world/meta.json +0 -4
- package/docs/api-reference/workflow-api/world/observability.mdx +0 -164
- package/docs/api-reference/workflow-api/world/queue.mdx +0 -86
- package/docs/deploying/building-a-world.mdx +0 -251
- package/docs/deploying/index.mdx +0 -95
- package/docs/deploying/meta.json +0 -4
- package/docs/deploying/world/local-world.mdx +0 -84
- package/docs/deploying/world/meta.json +0 -4
- package/docs/deploying/world/postgres-world.mdx +0 -224
- package/docs/deploying/world/vercel-world.mdx +0 -179
- package/docs/migration-guides/index.mdx +0 -34
- package/docs/migration-guides/meta.json +0 -9
- package/docs/migration-guides/migrating-from-aws-step-functions.mdx +0 -363
- package/docs/migration-guides/migrating-from-inngest.mdx +0 -314
- package/docs/migration-guides/migrating-from-temporal.mdx +0 -318
- package/docs/migration-guides/migrating-from-trigger-dev.mdx +0 -337
|
@@ -9,21 +9,25 @@ related:
|
|
|
9
9
|
- /docs/api-reference/workflow/sleep
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
<CopyPrompt
|
|
13
|
+
text="Fix timer usage inside workflow functions. Search `"use workflow"` functions for `setTimeout`, `setInterval`, `timers/promises`, polling loops, or `AbortSignal.timeout()`. Replace workflow delays with `await sleep("5s")`, `await sleep("24h")`, or `await sleep(date)` from `workflow`. For polling, use a workflow loop that calls a `"use step"` helper to check external state and then `await sleep(...)` between attempts. If a step itself needs a short in-process delay, keep that timer inside the `"use step"` function only. Verify the workflow can replay and resume after the durable sleep."
|
|
14
|
+
/>
|
|
15
|
+
|
|
12
16
|
This error occurs when you try to use `setTimeout()`, `setInterval()`, or related timing functions directly inside a workflow function.
|
|
13
17
|
|
|
14
|
-
## Error
|
|
18
|
+
## Error message
|
|
15
19
|
|
|
16
|
-
```
|
|
20
|
+
```text
|
|
17
21
|
Timeout functions like "setTimeout" and "setInterval" are not supported in workflow functions. Use the "sleep" function from "workflow" for time-based delays.
|
|
18
22
|
```
|
|
19
23
|
|
|
20
|
-
## Why
|
|
24
|
+
## Why this happens
|
|
21
25
|
|
|
22
26
|
Workflow functions run in a sandboxed environment where timing functions like `setTimeout()` and `setInterval()` are not available. These functions rely on asynchronous scheduling that would break the **deterministic replay** guarantees that workflows depend on.
|
|
23
27
|
|
|
24
28
|
When a workflow suspends and later resumes, it replays from the event log. If timing functions were allowed, the replay would produce different results than the original execution.
|
|
25
29
|
|
|
26
|
-
## Quick
|
|
30
|
+
## Quick fix
|
|
27
31
|
|
|
28
32
|
Use the `sleep` function from the `workflow` package for time-based delays. Unlike `setTimeout()`, `sleep` is tracked in the event log and replays correctly.
|
|
29
33
|
|
|
@@ -55,7 +59,7 @@ export async function delayedWorkflow() {
|
|
|
55
59
|
}
|
|
56
60
|
```
|
|
57
61
|
|
|
58
|
-
## Unavailable
|
|
62
|
+
## Unavailable functions
|
|
59
63
|
|
|
60
64
|
These timing functions cannot be used in workflow functions:
|
|
61
65
|
|
|
@@ -66,9 +70,9 @@ These timing functions cannot be used in workflow functions:
|
|
|
66
70
|
- `clearInterval()`
|
|
67
71
|
- `clearImmediate()`
|
|
68
72
|
|
|
69
|
-
## Common
|
|
73
|
+
## Common scenarios
|
|
70
74
|
|
|
71
|
-
### Polling with
|
|
75
|
+
### Polling with delays
|
|
72
76
|
|
|
73
77
|
If you need to poll an external service with delays between requests:
|
|
74
78
|
|
|
@@ -98,7 +102,7 @@ async function checkStatus() {
|
|
|
98
102
|
}
|
|
99
103
|
```
|
|
100
104
|
|
|
101
|
-
### Scheduled
|
|
105
|
+
### Scheduled delays
|
|
102
106
|
|
|
103
107
|
For workflows that need to wait for a specific duration:
|
|
104
108
|
|
|
@@ -11,13 +11,13 @@ related:
|
|
|
11
11
|
|
|
12
12
|
This error occurs when you provide an invalid value for the `respondWith` option when creating a webhook. The `respondWith` option must be either `"manual"` or a `Response` object.
|
|
13
13
|
|
|
14
|
-
## Error
|
|
14
|
+
## Error message
|
|
15
15
|
|
|
16
|
-
```
|
|
16
|
+
```text
|
|
17
17
|
Invalid `respondWith` value: [value]
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
## Why
|
|
20
|
+
## Why this happens
|
|
21
21
|
|
|
22
22
|
When creating a webhook with `createWebhook()`, you can specify how the webhook should respond to incoming HTTP requests using the `respondWith` option. This option only accepts specific values:
|
|
23
23
|
|
|
@@ -25,16 +25,16 @@ When creating a webhook with `createWebhook()`, you can specify how the webhook
|
|
|
25
25
|
2. A `Response` object - A pre-defined response to send immediately
|
|
26
26
|
3. `undefined` (default) - Returns a `202 Accepted` response
|
|
27
27
|
|
|
28
|
-
## Common
|
|
28
|
+
## Common causes
|
|
29
29
|
|
|
30
|
-
### Using an
|
|
30
|
+
### Using an invalid string value
|
|
31
31
|
|
|
32
32
|
```typescript lineNumbers
|
|
33
33
|
// Error - invalid string value
|
|
34
34
|
export async function webhookWorkflow() {
|
|
35
35
|
"use workflow";
|
|
36
36
|
|
|
37
|
-
const webhook =
|
|
37
|
+
const webhook = createWebhook({
|
|
38
38
|
respondWith: "automatic", // Error! // [!code highlight]
|
|
39
39
|
});
|
|
40
40
|
}
|
|
@@ -49,7 +49,7 @@ import { createWebhook } from "workflow";
|
|
|
49
49
|
export async function webhookWorkflow() {
|
|
50
50
|
"use workflow";
|
|
51
51
|
|
|
52
|
-
const webhook =
|
|
52
|
+
const webhook = createWebhook({
|
|
53
53
|
respondWith: "manual", // [!code highlight]
|
|
54
54
|
});
|
|
55
55
|
|
|
@@ -60,14 +60,14 @@ export async function webhookWorkflow() {
|
|
|
60
60
|
}
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
### Using a
|
|
63
|
+
### Using a non-Response object
|
|
64
64
|
|
|
65
65
|
```typescript lineNumbers
|
|
66
66
|
// Error - plain object instead of Response
|
|
67
67
|
export async function webhookWorkflow() {
|
|
68
68
|
"use workflow";
|
|
69
69
|
|
|
70
|
-
const webhook =
|
|
70
|
+
const webhook = createWebhook({
|
|
71
71
|
respondWith: { status: 200, body: "OK" }, // Error! // [!code highlight]
|
|
72
72
|
});
|
|
73
73
|
}
|
|
@@ -82,32 +82,32 @@ import { createWebhook } from "workflow";
|
|
|
82
82
|
export async function webhookWorkflow() {
|
|
83
83
|
"use workflow";
|
|
84
84
|
|
|
85
|
-
const webhook =
|
|
85
|
+
const webhook = createWebhook({
|
|
86
86
|
respondWith: new Response("OK", { status: 200 }), // [!code highlight]
|
|
87
87
|
});
|
|
88
88
|
}
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
-
## Valid
|
|
91
|
+
## Valid usage examples
|
|
92
92
|
|
|
93
|
-
### Default
|
|
93
|
+
### Default behavior (202 response)
|
|
94
94
|
|
|
95
95
|
```typescript lineNumbers
|
|
96
96
|
import { createWebhook } from "workflow";
|
|
97
97
|
|
|
98
98
|
// Returns 202 Accepted automatically
|
|
99
|
-
const webhook =
|
|
99
|
+
const webhook = createWebhook();
|
|
100
100
|
const request = await webhook;
|
|
101
101
|
// No need to send a response
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
-
### Manual
|
|
104
|
+
### Manual response
|
|
105
105
|
|
|
106
106
|
```typescript lineNumbers
|
|
107
107
|
import { createWebhook } from "workflow";
|
|
108
108
|
|
|
109
109
|
// Manual response control
|
|
110
|
-
const webhook =
|
|
110
|
+
const webhook = createWebhook({
|
|
111
111
|
respondWith: "manual",
|
|
112
112
|
});
|
|
113
113
|
|
|
@@ -125,13 +125,13 @@ await request.respondWith(
|
|
|
125
125
|
);
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
### Pre-defined
|
|
128
|
+
### Pre-defined response
|
|
129
129
|
|
|
130
130
|
```typescript lineNumbers
|
|
131
131
|
import { createWebhook } from "workflow";
|
|
132
132
|
|
|
133
133
|
// Immediate response
|
|
134
|
-
const webhook =
|
|
134
|
+
const webhook = createWebhook({
|
|
135
135
|
respondWith: new Response("Request received", { status: 200 }),
|
|
136
136
|
});
|
|
137
137
|
|
|
@@ -139,7 +139,7 @@ const request = await webhook;
|
|
|
139
139
|
// Response already sent
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
-
## Learn
|
|
142
|
+
## Learn more
|
|
143
143
|
|
|
144
144
|
- [createWebhook() API Reference](/docs/api-reference/workflow/create-webhook)
|
|
145
145
|
- [resumeWebhook() API Reference](/docs/api-reference/workflow-api/resume-webhook)
|
|
@@ -9,30 +9,34 @@ related:
|
|
|
9
9
|
- /docs/api-reference/workflow/create-webhook
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
<CopyPrompt
|
|
13
|
+
text="Fix webhook-response-not-sent errors. Find `createWebhook({ respondWith: "manual" })` usage. In the workflow, await the webhook request and pass the `RequestWithResponse` into a `"use step"` helper for validation and side effects. In every success, validation failure, catch, and early-return branch, call `await request.respondWith(new Response(...))` or `await request.respondWith(Response.json(...))` exactly once before the webhook completes. If manual control is not needed, remove `respondWith: "manual"` or use a static `Response` option. Add tests or local webhook calls for success, invalid input, and thrown-error paths."
|
|
14
|
+
/>
|
|
15
|
+
|
|
12
16
|
This error occurs when a webhook is configured with `respondWith: "manual"` but the workflow does not send a response using `request.respondWith()` before the webhook execution completes.
|
|
13
17
|
|
|
14
|
-
## Error
|
|
18
|
+
## Error message
|
|
15
19
|
|
|
16
|
-
```
|
|
20
|
+
```text
|
|
17
21
|
Workflow run did not send a response
|
|
18
22
|
```
|
|
19
23
|
|
|
20
|
-
## Why
|
|
24
|
+
## Why this happens
|
|
21
25
|
|
|
22
26
|
When you create a webhook with `respondWith: "manual"`, you are responsible for calling `request.respondWith()` to send the HTTP response back to the caller. If the workflow execution completes without sending a response, this error will be thrown.
|
|
23
27
|
|
|
24
28
|
The webhook infrastructure waits for a response to be sent, and if none is provided, it cannot complete the HTTP request properly.
|
|
25
29
|
|
|
26
|
-
## Common
|
|
30
|
+
## Common causes
|
|
27
31
|
|
|
28
|
-
### Forgetting to
|
|
32
|
+
### Forgetting to call `request.respondWith()`
|
|
29
33
|
|
|
30
34
|
```typescript lineNumbers
|
|
31
35
|
// Error - no response sent
|
|
32
36
|
export async function webhookWorkflow() {
|
|
33
37
|
"use workflow";
|
|
34
38
|
|
|
35
|
-
const webhook =
|
|
39
|
+
const webhook = createWebhook({
|
|
36
40
|
respondWith: "manual",
|
|
37
41
|
});
|
|
38
42
|
|
|
@@ -55,7 +59,7 @@ import { createWebhook } from "workflow";
|
|
|
55
59
|
export async function webhookWorkflow() {
|
|
56
60
|
"use workflow";
|
|
57
61
|
|
|
58
|
-
const webhook =
|
|
62
|
+
const webhook = createWebhook({
|
|
59
63
|
respondWith: "manual",
|
|
60
64
|
});
|
|
61
65
|
|
|
@@ -70,14 +74,14 @@ export async function webhookWorkflow() {
|
|
|
70
74
|
}
|
|
71
75
|
```
|
|
72
76
|
|
|
73
|
-
### Conditional
|
|
77
|
+
### Conditional response logic
|
|
74
78
|
|
|
75
79
|
```typescript lineNumbers
|
|
76
80
|
// Error - response only sent in some branches
|
|
77
81
|
export async function webhookWorkflow() {
|
|
78
82
|
"use workflow";
|
|
79
83
|
|
|
80
|
-
const webhook =
|
|
84
|
+
const webhook = createWebhook({
|
|
81
85
|
respondWith: "manual",
|
|
82
86
|
});
|
|
83
87
|
|
|
@@ -100,7 +104,7 @@ import { createWebhook } from "workflow";
|
|
|
100
104
|
export async function webhookWorkflow() {
|
|
101
105
|
"use workflow";
|
|
102
106
|
|
|
103
|
-
const webhook =
|
|
107
|
+
const webhook = createWebhook({
|
|
104
108
|
respondWith: "manual",
|
|
105
109
|
});
|
|
106
110
|
|
|
@@ -115,14 +119,14 @@ export async function webhookWorkflow() {
|
|
|
115
119
|
}
|
|
116
120
|
```
|
|
117
121
|
|
|
118
|
-
### Exception
|
|
122
|
+
### Exception before response
|
|
119
123
|
|
|
120
124
|
```typescript lineNumbers
|
|
121
125
|
// Error - exception thrown before response
|
|
122
126
|
export async function webhookWorkflow() {
|
|
123
127
|
"use workflow";
|
|
124
128
|
|
|
125
|
-
const webhook =
|
|
129
|
+
const webhook = createWebhook({
|
|
126
130
|
respondWith: "manual",
|
|
127
131
|
});
|
|
128
132
|
|
|
@@ -145,7 +149,7 @@ import { createWebhook } from "workflow";
|
|
|
145
149
|
export async function webhookWorkflow() {
|
|
146
150
|
"use workflow";
|
|
147
151
|
|
|
148
|
-
const webhook =
|
|
152
|
+
const webhook = createWebhook({
|
|
149
153
|
respondWith: "manual",
|
|
150
154
|
});
|
|
151
155
|
|
|
@@ -164,7 +168,7 @@ export async function webhookWorkflow() {
|
|
|
164
168
|
}
|
|
165
169
|
```
|
|
166
170
|
|
|
167
|
-
## Alternative: Use
|
|
171
|
+
## Alternative: Use default response mode
|
|
168
172
|
|
|
169
173
|
If you don't need custom response control, consider using the default response mode which automatically returns a `202 Accepted` response:
|
|
170
174
|
|
|
@@ -175,7 +179,7 @@ import { createWebhook } from "workflow";
|
|
|
175
179
|
export async function webhookWorkflow() {
|
|
176
180
|
"use workflow";
|
|
177
181
|
|
|
178
|
-
const webhook =
|
|
182
|
+
const webhook = createWebhook(); // [!code highlight]
|
|
179
183
|
const request = await webhook;
|
|
180
184
|
|
|
181
185
|
// Process request asynchronously
|
|
@@ -185,7 +189,7 @@ export async function webhookWorkflow() {
|
|
|
185
189
|
}
|
|
186
190
|
```
|
|
187
191
|
|
|
188
|
-
## Learn
|
|
192
|
+
## Learn more
|
|
189
193
|
|
|
190
194
|
- [createWebhook() API Reference](/docs/api-reference/workflow/create-webhook)
|
|
191
195
|
- [resumeWebhook() API Reference](/docs/api-reference/workflow-api/resume-webhook)
|
|
@@ -12,19 +12,19 @@ related:
|
|
|
12
12
|
|
|
13
13
|
This error occurs when the Workflow runtime tries to execute a workflow function that is not registered in the current deployment. When this happens, the run fails with a `RUNTIME_ERROR` error code.
|
|
14
14
|
|
|
15
|
-
## Error
|
|
15
|
+
## Error message
|
|
16
16
|
|
|
17
|
-
```
|
|
17
|
+
```text
|
|
18
18
|
Workflow "<workflowName>" is not registered in the current deployment.
|
|
19
19
|
This usually means a run was started against a deployment that does not
|
|
20
20
|
have this workflow, or there was a build/bundling issue.
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
## Why
|
|
23
|
+
## Why this happens
|
|
24
24
|
|
|
25
25
|
This error means the deployment that received the workflow execution request does not have the specified workflow function in its bundle. This is an **infrastructure error**, not a user code error.
|
|
26
26
|
|
|
27
|
-
## Common
|
|
27
|
+
## Common causes
|
|
28
28
|
|
|
29
29
|
### Run started against a deployment without the workflow
|
|
30
30
|
|
|
@@ -57,7 +57,7 @@ Something went wrong during the build process that caused the workflow function
|
|
|
57
57
|
- The workflow function is not exported from the workflow file
|
|
58
58
|
- An esbuild or SWC plugin error silently excluded the workflow
|
|
59
59
|
|
|
60
|
-
## How to
|
|
60
|
+
## How to resolve
|
|
61
61
|
|
|
62
62
|
1. **If the workflow was renamed or moved:** Deploy with the workflow restored to its original name and location, then retry the run. Alternatively, start a new run using the updated workflow name against the current deployment.
|
|
63
63
|
|
|
@@ -6,7 +6,6 @@ summary: Cancel in-flight work with AbortSignal or stop entire workflow runs.
|
|
|
6
6
|
prerequisites:
|
|
7
7
|
- /docs/foundations/workflows-and-steps
|
|
8
8
|
related:
|
|
9
|
-
- /docs/foundations/common-patterns
|
|
10
9
|
- /docs/foundations/hooks
|
|
11
10
|
- /docs/how-it-works/cancellation
|
|
12
11
|
---
|
|
@@ -15,7 +14,7 @@ Workflow DevKit supports two cancellation mechanisms: **AbortSignal** for fine-g
|
|
|
15
14
|
|
|
16
15
|
## AbortSignal
|
|
17
16
|
|
|
18
|
-
`AbortController` and `AbortSignal` work across workflow and step boundaries. Create an `AbortController` with `new AbortController()` in a workflow function, pass its signal to steps, and call `abort()`
|
|
17
|
+
`AbortController` and `AbortSignal` work across workflow and step boundaries. Create an `AbortController` with `new AbortController()` in a workflow function, pass its signal to steps, and call `abort()` using the standard [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) API.
|
|
19
18
|
|
|
20
19
|
```typescript lineNumbers
|
|
21
20
|
import { sleep } from "workflow";
|
|
@@ -49,17 +48,17 @@ async function longRunningStep(signal: AbortSignal) {
|
|
|
49
48
|
}
|
|
50
49
|
```
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
Use the standard `AbortController` API without special imports or wrapper functions.
|
|
53
52
|
|
|
54
53
|
<Callout type="info">
|
|
55
|
-
Cancellation is **cooperative**. Aborting a signal doesn't forcefully kill a step
|
|
54
|
+
Cancellation is **cooperative**. Aborting a signal doesn't forcefully kill a step. The step's code must check `signal.aborted` or pass the signal to APIs like `fetch` that respect it. If a step ignores the signal, it runs to completion.
|
|
56
55
|
</Callout>
|
|
57
56
|
|
|
58
57
|
<Callout type="info">
|
|
59
58
|
To learn how `AbortController` works durably across workflow suspensions, replays, and step boundaries, see [How Cancellation Works](/docs/how-it-works/cancellation).
|
|
60
59
|
</Callout>
|
|
61
60
|
|
|
62
|
-
### Timeout with
|
|
61
|
+
### Timeout with cancellation
|
|
63
62
|
|
|
64
63
|
Race a step against a timeout, and cancel the step if the timeout wins:
|
|
65
64
|
|
|
@@ -91,7 +90,7 @@ async function fetchUrl(url: string, signal: AbortSignal) {
|
|
|
91
90
|
}
|
|
92
91
|
```
|
|
93
92
|
|
|
94
|
-
### Cancelling
|
|
93
|
+
### Cancelling parallel work
|
|
95
94
|
|
|
96
95
|
When racing multiple steps, cancel the losers:
|
|
97
96
|
|
|
@@ -117,7 +116,7 @@ async function fetchUrl(url: string, signal: AbortSignal) {
|
|
|
117
116
|
}
|
|
118
117
|
```
|
|
119
118
|
|
|
120
|
-
### Passing
|
|
119
|
+
### Passing signal through a pipeline
|
|
121
120
|
|
|
122
121
|
Pass the same signal to a chain of steps. Aborting cancels whichever step is currently running:
|
|
123
122
|
|
|
@@ -176,7 +175,7 @@ async function uploadData(data: ArrayBuffer, signal: AbortSignal) {
|
|
|
176
175
|
}
|
|
177
176
|
```
|
|
178
177
|
|
|
179
|
-
### Step-
|
|
178
|
+
### Step-initiated abort
|
|
180
179
|
|
|
181
180
|
A step can receive the full `AbortController` and call `abort()` to cancel parallel work. This is useful for watchdog/monitor patterns where one step observes an external condition and cancels other in-flight steps:
|
|
182
181
|
|
|
@@ -221,7 +220,7 @@ async function monitorQuota(userId: string, controller: AbortController) {
|
|
|
221
220
|
}
|
|
222
221
|
```
|
|
223
222
|
|
|
224
|
-
### User-
|
|
223
|
+
### User-triggered cancellation with hooks
|
|
225
224
|
|
|
226
225
|
Combine hooks with abort controllers to let users cancel in-flight work from an external API:
|
|
227
226
|
|
|
@@ -267,23 +266,23 @@ export async function POST(request: Request) {
|
|
|
267
266
|
}
|
|
268
267
|
```
|
|
269
268
|
|
|
270
|
-
### How
|
|
269
|
+
### How steps handle abort
|
|
271
270
|
|
|
272
271
|
When an `AbortSignal` is aborted, the behavior depends on how the step uses it:
|
|
273
272
|
|
|
274
|
-
| Usage | Behavior on
|
|
273
|
+
| Usage | Behavior on abort |
|
|
275
274
|
|-------|-------------------|
|
|
276
|
-
| `fetch(url, { signal })` | Request is
|
|
275
|
+
| `fetch(url, { signal })` | Request is canceled, throws `AbortError` |
|
|
277
276
|
| `signal.throwIfAborted()` | Throws the abort reason |
|
|
278
277
|
| `signal.aborted` check | Returns `true`, step can exit gracefully |
|
|
279
278
|
| `signal.addEventListener('abort', fn)` | Callback fires, step can clean up |
|
|
280
279
|
| Ignored | Step runs to completion (abort is cooperative) |
|
|
281
280
|
|
|
282
|
-
### Abort
|
|
281
|
+
### Abort errors skip retries
|
|
283
282
|
|
|
284
|
-
When a step throws due to an abort (
|
|
283
|
+
When a step throws due to an abort (for example, `fetch` throws `AbortError`, or `signal.throwIfAborted()` throws), the runtime wraps the error in a `FatalError`. The step **skips retries**, and the error immediately bubbles up to the workflow.
|
|
285
284
|
|
|
286
|
-
|
|
285
|
+
An abort is an intentional cancellation, so retrying the step would result in another abort. You don't need to manually wrap abort errors in `FatalError`.
|
|
287
286
|
|
|
288
287
|
```typescript lineNumbers
|
|
289
288
|
import { sleep } from "workflow";
|
|
@@ -300,7 +299,7 @@ export async function workflow() {
|
|
|
300
299
|
if (result === null) controller.abort();
|
|
301
300
|
return result;
|
|
302
301
|
} catch (err) {
|
|
303
|
-
// AbortError arrives as FatalError
|
|
302
|
+
// AbortError arrives as FatalError, with no retries attempted // [!code highlight]
|
|
304
303
|
return { status: "cancelled" };
|
|
305
304
|
}
|
|
306
305
|
}
|
|
@@ -313,7 +312,7 @@ async function cancellableStep(signal: AbortSignal) {
|
|
|
313
312
|
}
|
|
314
313
|
```
|
|
315
314
|
|
|
316
|
-
### Passing AbortSignal as
|
|
315
|
+
### Passing AbortSignal as workflow input
|
|
317
316
|
|
|
318
317
|
You can pass an `AbortSignal` from external code into a workflow via `start()`:
|
|
319
318
|
|
|
@@ -330,15 +329,15 @@ export async function POST(request: Request) {
|
|
|
330
329
|
}
|
|
331
330
|
```
|
|
332
331
|
|
|
333
|
-
When the signal is serialized at the `start()` boundary, an event listener
|
|
332
|
+
When the signal is serialized at the `start()` boundary, an event listener attaches to the external signal and writes the cancellation packet to the backing stream. The external `abort()` propagates into the workflow only while the originating process is still alive (the same constraint as passing a `ReadableStream` as input).
|
|
334
333
|
|
|
335
334
|
<Callout type="info">
|
|
336
335
|
For reliable external cancellation that works regardless of process lifetime, prefer the [User-Triggered Cancellation with Hooks](#user-triggered-cancellation-with-hooks) pattern. Hooks are durable and don't depend on the caller's process staying alive.
|
|
337
336
|
</Callout>
|
|
338
337
|
|
|
339
|
-
## Run
|
|
338
|
+
## Run cancellation
|
|
340
339
|
|
|
341
|
-
Run cancellation stops an entire workflow at the next suspension point. Unlike `AbortSignal`, it is not cooperative
|
|
340
|
+
Run cancellation stops an entire workflow at the next suspension point. Unlike `AbortSignal`, it is not cooperative. The workflow does not continue executing after cancellation.
|
|
342
341
|
|
|
343
342
|
```typescript title="app/api/cancel-run/route.ts" lineNumbers
|
|
344
343
|
import { getRun } from "workflow/api";
|
|
@@ -354,10 +353,10 @@ export async function POST(request: Request) {
|
|
|
354
353
|
```
|
|
355
354
|
|
|
356
355
|
<Callout type="info">
|
|
357
|
-
Calling `run.cancel()` is the same action as clicking the **Cancel** button on a run in the observability UI
|
|
356
|
+
Calling `run.cancel()` is the same action as clicking the **Cancel** button on a run in the observability UI. Both produce identical `run_cancelled` events in the event log.
|
|
358
357
|
</Callout>
|
|
359
358
|
|
|
360
|
-
When a run is
|
|
359
|
+
When a run is canceled:
|
|
361
360
|
- The workflow stops at its next suspension point (step call, hook await, or sleep)
|
|
362
361
|
- A `run_cancelled` event is recorded in the [event log](/docs/how-it-works/event-sourcing)
|
|
363
362
|
- All associated hooks are disposed and their tokens released
|
|
@@ -367,19 +366,19 @@ When a run is cancelled:
|
|
|
367
366
|
Run cancellation does **not** automatically abort any outstanding `AbortSignal`s. Steps that are currently executing will run to completion. If you need in-flight cancellation of specific operations, use `AbortSignal`.
|
|
368
367
|
</Callout>
|
|
369
368
|
|
|
370
|
-
## AbortSignal vs.
|
|
369
|
+
## AbortSignal vs. run cancellation
|
|
371
370
|
|
|
372
371
|
| | AbortSignal | Run Cancellation |
|
|
373
372
|
|---|---|---|
|
|
374
373
|
| **Scope** | Individual operations within a step | Entire workflow run |
|
|
375
374
|
| **Triggered by** | Your code (`controller.abort()`) | External API (`run.cancel()`) |
|
|
376
|
-
| **Cooperative** | Yes
|
|
375
|
+
| **Cooperative** | Yes. Steps must check the signal | No. The workflow stops at the next suspension point |
|
|
377
376
|
| **Granularity** | Can target specific steps or operations | All-or-nothing |
|
|
378
377
|
| **In-flight steps** | Aborted immediately if using the signal | Run to completion |
|
|
379
378
|
|
|
380
379
|
Use `AbortSignal` when you need fine-grained, in-flight cancellation of specific operations. Use run cancellation when you want to stop the entire workflow.
|
|
381
380
|
|
|
382
|
-
## Best
|
|
381
|
+
## Best practices
|
|
383
382
|
|
|
384
383
|
**Use `throwIfAborted()` before expensive work.** This throws the signal's abort reason if the signal is already aborted, preventing wasted compute:
|
|
385
384
|
|
|
@@ -449,12 +448,12 @@ export async function workflow() {
|
|
|
449
448
|
}
|
|
450
449
|
```
|
|
451
450
|
|
|
452
|
-
This is safe even if both steps have already completed
|
|
451
|
+
This is safe even if both steps have already completed because aborting a finished operation is a no-op.
|
|
453
452
|
|
|
454
|
-
## Related
|
|
453
|
+
## Related documentation
|
|
455
454
|
|
|
456
|
-
- [How Cancellation Works](/docs/how-it-works/cancellation)
|
|
457
|
-
- [Serialization](/docs/foundations/serialization)
|
|
458
|
-
- [
|
|
459
|
-
- [Hooks](/docs/foundations/hooks)
|
|
460
|
-
- [Errors and Retries](/docs/foundations/errors-and-retries)
|
|
455
|
+
- [How Cancellation Works](/docs/how-it-works/cancellation): Hook and stream backing, serialization internals
|
|
456
|
+
- [Serialization](/docs/foundations/serialization): Understanding serializable types
|
|
457
|
+
- [Cookbook](/cookbook): Timeout, race, and other reliability patterns
|
|
458
|
+
- [Hooks](/docs/foundations/hooks): Pausing workflows for external events
|
|
459
|
+
- [Errors and Retries](/docs/foundations/errors-and-retries): Handling step failures
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Errors & Retrying
|
|
3
|
-
description: Customize retry behavior with FatalError and RetryableError
|
|
3
|
+
description: Customize retry behavior with FatalError and RetryableError.
|
|
4
4
|
type: conceptual
|
|
5
5
|
summary: Control how steps handle failures and customize retry behavior.
|
|
6
6
|
prerequisites:
|
|
@@ -12,7 +12,7 @@ related:
|
|
|
12
12
|
|
|
13
13
|
By default, errors thrown inside steps are retried. Additionally, Workflow SDK provides two new types of errors you can use to customize retries.
|
|
14
14
|
|
|
15
|
-
## Default
|
|
15
|
+
## Default retrying
|
|
16
16
|
|
|
17
17
|
By default, steps retry up to 3 times on arbitrary errors. You can customize the number of retries by adding a `maxRetries` property to the step function.
|
|
18
18
|
|
|
@@ -42,9 +42,9 @@ Steps get enqueued immediately after a failure. Read on to see how this can be c
|
|
|
42
42
|
more information.
|
|
43
43
|
</Callout>
|
|
44
44
|
|
|
45
|
-
## Intentional
|
|
45
|
+
## Intentional errors
|
|
46
46
|
|
|
47
|
-
When your step needs to intentionally throw an error and skip retrying,
|
|
47
|
+
When your step needs to intentionally throw an error and skip retrying, throw a [`FatalError`](/docs/api-reference/workflow/fatal-error).
|
|
48
48
|
|
|
49
49
|
```typescript lineNumbers
|
|
50
50
|
import { FatalError } from "workflow";
|
|
@@ -67,7 +67,7 @@ async function callApi(endpoint: string) {
|
|
|
67
67
|
}
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
## Customize
|
|
70
|
+
## Customize retry behavior
|
|
71
71
|
|
|
72
72
|
When you need to customize the delay on a retry, use [`RetryableError`](/docs/api-reference/workflow/retryable-error) and set the `retryAfter` property.
|
|
73
73
|
|
|
@@ -97,7 +97,7 @@ async function callApi(endpoint: string) {
|
|
|
97
97
|
}
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
## Advanced
|
|
100
|
+
## Advanced example
|
|
101
101
|
|
|
102
102
|
This final example combines everything we've learned, along with [`getStepMetadata`](/docs/api-reference/workflow/get-step-metadata).
|
|
103
103
|
|
|
@@ -139,7 +139,32 @@ callApi.maxRetries = 5; // Retry up to 5 times on failure (6 total attempts)
|
|
|
139
139
|
step can run up to 4 times total (1 initial attempt + 3 retries).
|
|
140
140
|
</Callout>
|
|
141
141
|
|
|
142
|
-
##
|
|
142
|
+
## Serialization failures
|
|
143
|
+
|
|
144
|
+
A step whose arguments or return value cannot be [serialized](/docs/foundations/serialization) fails like a step whose body threw a `FatalError`: the failure is deterministic, so it skips the retry loop, and a `try/catch` around the step call observes the `SerializationError`:
|
|
145
|
+
|
|
146
|
+
```typescript lineNumbers
|
|
147
|
+
async function someStep(input: unknown) {
|
|
148
|
+
"use step";
|
|
149
|
+
return input;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export async function myWorkflow(input: unknown) {
|
|
153
|
+
"use workflow";
|
|
154
|
+
|
|
155
|
+
try {
|
|
156
|
+
await someStep(input);
|
|
157
|
+
} catch (err) {
|
|
158
|
+
if ((err as Error).name === "SerializationError") {
|
|
159
|
+
// e.g. `Failed to serialize step arguments at path "..."`
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Uncaught, the run fails immediately with the `USER_ERROR` code, without retrying. See [serialization-failed](/docs/errors/serialization-failed) for common causes and fixes.
|
|
166
|
+
|
|
167
|
+
## Error codes
|
|
143
168
|
|
|
144
169
|
When a workflow run fails, the error includes an `errorCode` that classifies the failure, alongside the original thrown value (preserved as `cause`):
|
|
145
170
|
|
|
@@ -153,7 +178,7 @@ try {
|
|
|
153
178
|
const result = await run.returnValue;
|
|
154
179
|
} catch (err) {
|
|
155
180
|
if (WorkflowRunFailedError.is(err)) {
|
|
156
|
-
console.log(err.errorCode); // "USER_ERROR", "
|
|
181
|
+
console.log(err.errorCode); // e.g. "USER_ERROR", "MAX_EVENTS_EXCEEDED", or undefined
|
|
157
182
|
// `cause` is the original thrown value, hydrated through the workflow
|
|
158
183
|
// serialization pipeline. It can be any thrown value, so check shape.
|
|
159
184
|
if (err.cause instanceof Error) {
|
|
@@ -166,13 +191,19 @@ try {
|
|
|
166
191
|
| Code | Meaning |
|
|
167
192
|
| --- | --- |
|
|
168
193
|
| `USER_ERROR` | An error thrown in your workflow or step code (including propagated step failures like `FatalError`) |
|
|
169
|
-
| `
|
|
194
|
+
| `MAX_EVENTS_EXCEEDED` | The run reached the World's per-run event ceiling (25,000 on the Local and Vercel Worlds). Split unbounded loops into [child workflows](/cookbook/advanced/child-workflows); see [Limits](/docs/configuration/runtime-tuning#limits) |
|
|
195
|
+
| `MAX_DELIVERIES_EXCEEDED` | The run exceeded the maximum number of queue deliveries |
|
|
196
|
+
| `REPLAY_TIMEOUT` | A workflow replay exceeded the maximum allowed duration |
|
|
197
|
+
| `REPLAY_DIVERGENCE` | A replay could not consume the event log deterministically, usually because of non-deterministic workflow code. |
|
|
198
|
+
| `CORRUPTED_EVENT_LOG` | The event log cannot be replayed: it contains orphaned or mismatched events, or one of its stored payloads is no longer readable from the World's storage. If you see this, please [file an issue](https://github.com/vercel/workflow/issues) |
|
|
199
|
+
| `WORLD_CONTRACT_ERROR` | A World response violated the SDK contract; points at a World implementation bug |
|
|
200
|
+
| `RUNTIME_ERROR` | An internal runtime error. If you see this, please [file an issue](https://github.com/vercel/workflow/issues) |
|
|
170
201
|
|
|
171
202
|
<Callout type="info">
|
|
172
|
-
The error code is also available on the run entity
|
|
203
|
+
The error code is also available on the run entity through the Workflow CLI (`npx workflow inspect runs <runId>`) in the `error.code` field, and as an OpenTelemetry span attribute (`workflow.error.code`) for observability.
|
|
173
204
|
</Callout>
|
|
174
205
|
|
|
175
|
-
## Rolling
|
|
206
|
+
## Rolling back failed steps
|
|
176
207
|
|
|
177
208
|
When a workflow fails partway through, it can leave the system in an inconsistent state.
|
|
178
209
|
A common pattern to address this is "rollbacks": for each successful step, record a corresponding rollback action that can undo it.
|