workflow 4.2.0-beta.70 → 4.2.0-beta.72
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/dist/api.d.ts +1 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +1 -1
- package/dist/internal/builtins.d.ts +3 -3
- package/dist/internal/builtins.d.ts.map +1 -1
- package/dist/internal/builtins.js +7 -7
- 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/observability.d.ts +20 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +20 -0
- package/docs/ai/chat-session-modeling.mdx +4 -4
- package/docs/ai/defining-tools.mdx +7 -1
- package/docs/ai/index.mdx +8 -5
- package/docs/ai/message-queueing.mdx +8 -6
- package/docs/ai/resumable-streams.mdx +37 -4
- package/docs/ai/sleep-and-delays.mdx +2 -0
- package/docs/api-reference/index.mdx +3 -0
- package/docs/api-reference/meta.json +1 -1
- package/docs/api-reference/workflow/define-hook.mdx +2 -0
- package/docs/api-reference/workflow/get-writable.mdx +1 -0
- package/docs/api-reference/workflow-ai/durable-agent.mdx +7 -5
- package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +2 -0
- package/docs/api-reference/workflow-api/get-run.mdx +14 -0
- package/docs/api-reference/workflow-api/get-world.mdx +105 -0
- package/docs/api-reference/workflow-api/start.mdx +24 -0
- package/docs/api-reference/workflow-errors/entity-conflict-error.mdx +60 -0
- package/docs/api-reference/workflow-errors/hook-not-found-error.mdx +90 -0
- package/docs/api-reference/workflow-errors/meta.json +16 -0
- package/docs/api-reference/workflow-errors/run-expired-error.mdx +58 -0
- package/docs/api-reference/workflow-errors/step-not-registered-error.mdx +56 -0
- package/docs/api-reference/workflow-errors/throttle-error.mdx +62 -0
- package/docs/api-reference/workflow-errors/too-early-error.mdx +62 -0
- package/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx +57 -0
- package/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx +56 -0
- package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +62 -0
- package/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx +56 -0
- package/docs/api-reference/workflow-errors/workflow-world-error.mdx +79 -0
- package/docs/api-reference/workflow-serde/index.mdx +52 -0
- package/docs/api-reference/workflow-serde/meta.json +3 -0
- package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +70 -0
- package/docs/api-reference/workflow-serde/workflow-serialize.mdx +75 -0
- package/docs/changelog/index.mdx +15 -0
- package/docs/changelog/meta.json +5 -0
- package/docs/deploying/building-a-world.mdx +20 -0
- package/docs/deploying/world/vercel-world.mdx +2 -1
- package/docs/errors/hook-conflict.mdx +9 -3
- package/docs/errors/index.mdx +6 -0
- package/docs/errors/step-not-registered.mdx +66 -0
- package/docs/errors/webhook-invalid-respond-with-value.mdx +10 -0
- package/docs/errors/webhook-response-not-sent.mdx +8 -0
- package/docs/errors/workflow-not-registered.mdx +64 -0
- package/docs/foundations/common-patterns.mdx +4 -0
- package/docs/foundations/errors-and-retries.mdx +29 -0
- package/docs/foundations/serialization.mdx +211 -0
- package/docs/foundations/streaming.mdx +22 -0
- package/docs/getting-started/index.mdx +3 -3
- package/docs/getting-started/meta.json +15 -0
- package/docs/getting-started/nestjs.mdx +4 -8
- package/docs/how-it-works/encryption.mdx +93 -0
- package/docs/how-it-works/meta.json +2 -1
- package/docs/observability/index.mdx +3 -0
- package/package.json +18 -12
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: WorkflowWorldError
|
|
3
|
+
description: Base error for failures from workflow storage backends.
|
|
4
|
+
type: reference
|
|
5
|
+
summary: Catch WorkflowWorldError to handle any error originating from a workflow world (storage backend).
|
|
6
|
+
related:
|
|
7
|
+
- /docs/api-reference/workflow-errors/entity-conflict-error
|
|
8
|
+
- /docs/api-reference/workflow-errors/run-expired-error
|
|
9
|
+
- /docs/api-reference/workflow-errors/too-early-error
|
|
10
|
+
- /docs/api-reference/workflow-errors/throttle-error
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
`WorkflowWorldError` is the base error class for failures originating from a workflow world (storage backend). World implementations (local, Postgres, Vercel) throw subclasses of this error when storage operations fail.
|
|
14
|
+
|
|
15
|
+
You can use `instanceof WorkflowWorldError` to catch any world-related error regardless of the specific type. Note that the static `.is()` method only matches errors constructed directly as `WorkflowWorldError` — use the subclass-specific `.is()` methods (e.g. `EntityConflictError.is()`) to match specific error types.
|
|
16
|
+
|
|
17
|
+
<Callout>
|
|
18
|
+
Most world errors are handled automatically by the Workflow runtime. You will typically only encounter these errors when interacting with world storage APIs directly or when there are infrastructure-level issues.
|
|
19
|
+
</Callout>
|
|
20
|
+
|
|
21
|
+
```typescript lineNumbers
|
|
22
|
+
import { WorkflowWorldError } from "workflow/errors"
|
|
23
|
+
declare const world: { events: { create(...args: any[]): Promise<any> } }; // @setup
|
|
24
|
+
declare const runId: string; // @setup
|
|
25
|
+
declare const event: any; // @setup
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
await world.events.create(runId, event);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (error instanceof WorkflowWorldError) { // [!code highlight]
|
|
31
|
+
console.error("Storage backend error:", error.message);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## API Signature
|
|
37
|
+
|
|
38
|
+
### Properties
|
|
39
|
+
|
|
40
|
+
<TSDoc
|
|
41
|
+
definition={`
|
|
42
|
+
interface WorkflowWorldError {
|
|
43
|
+
/** HTTP status code from the world backend, if available. */
|
|
44
|
+
status?: number;
|
|
45
|
+
/** Machine-readable error code, if available. */
|
|
46
|
+
code?: string;
|
|
47
|
+
/** The URL that was requested, if available. */
|
|
48
|
+
url?: string;
|
|
49
|
+
/** Retry-After value in seconds, present on 429 and 425 responses. */
|
|
50
|
+
retryAfter?: number;
|
|
51
|
+
/** The error message. */
|
|
52
|
+
message: string;
|
|
53
|
+
}
|
|
54
|
+
export default WorkflowWorldError;`}
|
|
55
|
+
/>
|
|
56
|
+
|
|
57
|
+
### Static Methods
|
|
58
|
+
|
|
59
|
+
#### `WorkflowWorldError.is(value)`
|
|
60
|
+
|
|
61
|
+
Type-safe check that matches only errors constructed directly as `WorkflowWorldError`. Does not match subclasses like `EntityConflictError` — use `instanceof` to catch all world errors, or the subclass-specific `.is()` methods.
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { WorkflowWorldError } from "workflow/errors"
|
|
65
|
+
declare const error: unknown; // @setup
|
|
66
|
+
|
|
67
|
+
if (WorkflowWorldError.is(error)) {
|
|
68
|
+
// error is typed as WorkflowWorldError (not subclasses)
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Subclasses
|
|
73
|
+
|
|
74
|
+
The following error types extend `WorkflowWorldError`:
|
|
75
|
+
|
|
76
|
+
- [`EntityConflictError`](/docs/api-reference/workflow-errors/entity-conflict-error) — operation conflicts with entity state
|
|
77
|
+
- [`RunExpiredError`](/docs/api-reference/workflow-errors/run-expired-error) — run has expired
|
|
78
|
+
- [`TooEarlyError`](/docs/api-reference/workflow-errors/too-early-error) — request made before system is ready
|
|
79
|
+
- [`ThrottleError`](/docs/api-reference/workflow-errors/throttle-error) — request was rate-limited
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "@workflow/serde"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Serialization symbols for custom class serialization in Workflow DevKit.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```package-install
|
|
10
|
+
npm i @workflow/serde
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
By default, Workflow DevKit can serialize standard JavaScript types like primitives, objects, arrays, `Date`, `Map`, `Set`, and more. However, custom class instances are not serializable by default because the serialization system doesn't know how to reconstruct them.
|
|
16
|
+
|
|
17
|
+
The `@workflow/serde` package provides two symbols that allow you to define custom serialization and deserialization logic for your classes, enabling them to be passed between workflow and step functions.
|
|
18
|
+
|
|
19
|
+
## Symbols
|
|
20
|
+
|
|
21
|
+
<Cards>
|
|
22
|
+
<Card href="/docs/api-reference/workflow-serde/workflow-serialize" title="WORKFLOW_SERIALIZE">
|
|
23
|
+
Symbol for defining how to serialize a class instance to plain data.
|
|
24
|
+
</Card>
|
|
25
|
+
<Card href="/docs/api-reference/workflow-serde/workflow-deserialize" title="WORKFLOW_DESERIALIZE">
|
|
26
|
+
Symbol for defining how to reconstruct a class instance from plain data.
|
|
27
|
+
</Card>
|
|
28
|
+
</Cards>
|
|
29
|
+
|
|
30
|
+
## Quick Example
|
|
31
|
+
|
|
32
|
+
{/* @expect-error:2351 */}
|
|
33
|
+
|
|
34
|
+
```typescript lineNumbers
|
|
35
|
+
import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from "@workflow/serde";
|
|
36
|
+
|
|
37
|
+
class Point {
|
|
38
|
+
constructor(public x: number, public y: number) {}
|
|
39
|
+
|
|
40
|
+
static [WORKFLOW_SERIALIZE](instance: Point) {
|
|
41
|
+
return { x: instance.x, y: instance.y };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static [WORKFLOW_DESERIALIZE](data: { x: number; y: number }) {
|
|
45
|
+
return new Point(data.x, data.y);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
<Callout>
|
|
51
|
+
For a complete guide on custom class serialization, see the [Serialization documentation](/docs/foundations/serialization#custom-class-serialization).
|
|
52
|
+
</Callout>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: WORKFLOW_DESERIALIZE
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
A symbol used to define custom deserialization for user-defined class instances. The static method should accept serialized data and return a new class instance.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
{/* @expect-error:2351 */}
|
|
10
|
+
|
|
11
|
+
```typescript lineNumbers
|
|
12
|
+
import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from "@workflow/serde";
|
|
13
|
+
|
|
14
|
+
class Point {
|
|
15
|
+
constructor(public x: number, public y: number) {}
|
|
16
|
+
|
|
17
|
+
static [WORKFLOW_SERIALIZE](instance: Point) {
|
|
18
|
+
return { x: instance.x, y: instance.y };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static [WORKFLOW_DESERIALIZE](data: { x: number; y: number }) {
|
|
22
|
+
return new Point(data.x, data.y);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## API Signature
|
|
28
|
+
|
|
29
|
+
{/* @skip-typecheck */}
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
static [WORKFLOW_DESERIALIZE](data: SerializableData): T
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Parameters
|
|
36
|
+
|
|
37
|
+
<TSDoc
|
|
38
|
+
definition={`
|
|
39
|
+
interface Parameters {
|
|
40
|
+
/**
|
|
41
|
+
* The serialized data to reconstruct into a class instance.
|
|
42
|
+
* This is the same data that was returned by WORKFLOW_SERIALIZE.
|
|
43
|
+
*/
|
|
44
|
+
data: SerializableData;
|
|
45
|
+
}
|
|
46
|
+
export default Parameters;`}
|
|
47
|
+
/>
|
|
48
|
+
|
|
49
|
+
### Returns
|
|
50
|
+
|
|
51
|
+
The method should return a new instance of the class, reconstructed from the serialized data.
|
|
52
|
+
|
|
53
|
+
## Requirements
|
|
54
|
+
|
|
55
|
+
<Callout type="warn">
|
|
56
|
+
The method must be implemented as a **static** method on the class. Instance methods are not supported.
|
|
57
|
+
</Callout>
|
|
58
|
+
|
|
59
|
+
- Both `WORKFLOW_SERIALIZE` and `WORKFLOW_DESERIALIZE` must be implemented together
|
|
60
|
+
- The method receives the exact data that was returned by `WORKFLOW_SERIALIZE`
|
|
61
|
+
- If `WORKFLOW_SERIALIZE` returns complex types (like `Map` or `Date`), they will be properly deserialized before being passed to this method
|
|
62
|
+
|
|
63
|
+
<Callout type="warn">
|
|
64
|
+
This method runs inside the workflow context and is subject to the same constraints as `"use workflow"` functions:
|
|
65
|
+
- No Node.js-specific APIs (like `fs`, `path`, `crypto`, etc.)
|
|
66
|
+
- No non-deterministic operations (like `Math.random()` or `Date.now()`)
|
|
67
|
+
- No external network calls
|
|
68
|
+
|
|
69
|
+
Keep this method simple and focused on reconstructing the instance from the provided data.
|
|
70
|
+
</Callout>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: WORKFLOW_SERIALIZE
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
A symbol used to define custom serialization for user-defined class instances. The static method should accept an instance and return serializable data.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
{/* @expect-error:2351 */}
|
|
10
|
+
|
|
11
|
+
```typescript lineNumbers
|
|
12
|
+
import { WORKFLOW_SERIALIZE, WORKFLOW_DESERIALIZE } from "@workflow/serde";
|
|
13
|
+
|
|
14
|
+
class Point {
|
|
15
|
+
constructor(public x: number, public y: number) {}
|
|
16
|
+
|
|
17
|
+
static [WORKFLOW_SERIALIZE](instance: Point) {
|
|
18
|
+
return { x: instance.x, y: instance.y };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static [WORKFLOW_DESERIALIZE](data: { x: number; y: number }) {
|
|
22
|
+
return new Point(data.x, data.y);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## API Signature
|
|
28
|
+
|
|
29
|
+
{/* @skip-typecheck */}
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
static [WORKFLOW_SERIALIZE](instance: T): SerializableData
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Parameters
|
|
36
|
+
|
|
37
|
+
<TSDoc
|
|
38
|
+
definition={`
|
|
39
|
+
interface Parameters {
|
|
40
|
+
/**
|
|
41
|
+
* The class instance to serialize.
|
|
42
|
+
*/
|
|
43
|
+
instance: T;
|
|
44
|
+
}
|
|
45
|
+
export default Parameters;`}
|
|
46
|
+
/>
|
|
47
|
+
|
|
48
|
+
### Returns
|
|
49
|
+
|
|
50
|
+
The method should return serializable data. This can be:
|
|
51
|
+
|
|
52
|
+
- Primitives (`string`, `number`, `boolean`, `null`, `undefined`, `bigint`)
|
|
53
|
+
- Plain objects with serializable values
|
|
54
|
+
- Arrays of serializable values
|
|
55
|
+
- Built-in serializable types (`Date`, `Map`, `Set`, `RegExp`, `URL`, etc.)
|
|
56
|
+
- Other custom classes that implement serialization
|
|
57
|
+
|
|
58
|
+
## Requirements
|
|
59
|
+
|
|
60
|
+
<Callout type="warn">
|
|
61
|
+
The method must be implemented as a **static** method on the class. Instance methods are not supported.
|
|
62
|
+
</Callout>
|
|
63
|
+
|
|
64
|
+
- Both `WORKFLOW_SERIALIZE` and `WORKFLOW_DESERIALIZE` must be implemented together
|
|
65
|
+
- The returned data must itself be serializable
|
|
66
|
+
- The SWC compiler plugin automatically detects and registers classes that implement these symbols
|
|
67
|
+
|
|
68
|
+
<Callout type="warn">
|
|
69
|
+
This method runs inside the workflow context and is subject to the same constraints as `"use workflow"` functions:
|
|
70
|
+
- No Node.js-specific APIs (like `fs`, `path`, `crypto`, etc.)
|
|
71
|
+
- No non-deterministic operations (like `Math.random()` or `Date.now()`)
|
|
72
|
+
- No external network calls
|
|
73
|
+
|
|
74
|
+
Keep this method simple and focused on extracting data from the instance.
|
|
75
|
+
</Callout>
|
|
@@ -180,11 +180,31 @@ interface Streamer {
|
|
|
180
180
|
): Promise<ReadableStream<Uint8Array>>;
|
|
181
181
|
|
|
182
182
|
listStreamsByRunId(runId: string): Promise<string[]>;
|
|
183
|
+
|
|
184
|
+
/** Paginated snapshot of stream chunks. */
|
|
185
|
+
getStreamChunks(
|
|
186
|
+
name: string,
|
|
187
|
+
runId: string,
|
|
188
|
+
options?: { limit?: number; cursor?: string }
|
|
189
|
+
): Promise<{
|
|
190
|
+
data: { index: number; data: Uint8Array }[];
|
|
191
|
+
cursor: string | null;
|
|
192
|
+
hasMore: boolean;
|
|
193
|
+
done: boolean;
|
|
194
|
+
}>;
|
|
195
|
+
|
|
196
|
+
/** Lightweight metadata: tail index and completion flag. */
|
|
197
|
+
getStreamInfo(
|
|
198
|
+
name: string,
|
|
199
|
+
runId: string
|
|
200
|
+
): Promise<{ tailIndex: number; done: boolean }>;
|
|
183
201
|
}
|
|
184
202
|
```
|
|
185
203
|
|
|
186
204
|
Streams are identified by a combination of `runId` and `name`. Each workflow run can have multiple named streams.
|
|
187
205
|
|
|
206
|
+
`getStreamChunks` returns a paginated snapshot of currently available chunks (unlike `readFromStream` which returns a live `ReadableStream` that waits for new chunks). `getStreamInfo` returns the tail index (last chunk index, 0-based, or `-1` when empty) and whether the stream is complete — useful for resolving negative `startIndex` values into absolute positions.
|
|
207
|
+
|
|
188
208
|
## Reference Implementations
|
|
189
209
|
|
|
190
210
|
Study these implementations for guidance:
|
|
@@ -6,6 +6,7 @@ summary: Deploy workflows to Vercel with fully-managed storage, queuing, and aut
|
|
|
6
6
|
prerequisites:
|
|
7
7
|
- /docs/deploying
|
|
8
8
|
related:
|
|
9
|
+
- /docs/how-it-works/encryption
|
|
9
10
|
- /docs/deploying/world/local-world
|
|
10
11
|
- /docs/deploying/world/postgres-world
|
|
11
12
|
---
|
|
@@ -125,7 +126,7 @@ This ensures long-running workflows complete reliably without being affected by
|
|
|
125
126
|
|
|
126
127
|
The Vercel World uses Vercel's infrastructure for workflow execution:
|
|
127
128
|
|
|
128
|
-
- **Storage** - Workflow data is stored in Vercel's cloud with automatic replication and encryption
|
|
129
|
+
- **Storage** - Workflow data is stored in Vercel's cloud with automatic replication and [end-to-end encryption](/docs/how-it-works/encryption)
|
|
129
130
|
- **Queuing** - Steps are distributed across serverless functions with automatic retries
|
|
130
131
|
- **Authentication** - OIDC tokens provide secure, automatic authentication
|
|
131
132
|
|
|
@@ -46,6 +46,8 @@ export async function processPayment() {
|
|
|
46
46
|
**Solution:** Use unique tokens that include the run ID or other unique identifiers.
|
|
47
47
|
|
|
48
48
|
```typescript lineNumbers
|
|
49
|
+
import { createHook } from "workflow";
|
|
50
|
+
|
|
49
51
|
export async function processPayment(orderId: string) {
|
|
50
52
|
"use workflow";
|
|
51
53
|
|
|
@@ -60,6 +62,8 @@ export async function processPayment(orderId: string) {
|
|
|
60
62
|
The safest approach is to let the Workflow runtime generate a unique token automatically:
|
|
61
63
|
|
|
62
64
|
```typescript lineNumbers
|
|
65
|
+
import { createHook } from "workflow";
|
|
66
|
+
|
|
63
67
|
export async function processPayment() {
|
|
64
68
|
"use workflow";
|
|
65
69
|
|
|
@@ -71,10 +75,11 @@ export async function processPayment() {
|
|
|
71
75
|
|
|
72
76
|
## Handling Hook Conflicts in Your Workflow
|
|
73
77
|
|
|
74
|
-
When a hook conflict occurs, awaiting the hook will throw a `
|
|
78
|
+
When a hook conflict occurs, awaiting the hook will throw a `HookConflictError`. You can catch this error to handle the conflict gracefully:
|
|
75
79
|
|
|
76
80
|
```typescript lineNumbers
|
|
77
|
-
import {
|
|
81
|
+
import { createHook } from "workflow";
|
|
82
|
+
import { HookConflictError } from "@workflow/errors";
|
|
78
83
|
|
|
79
84
|
export async function processPayment(orderId: string) {
|
|
80
85
|
"use workflow";
|
|
@@ -85,8 +90,9 @@ export async function processPayment(orderId: string) {
|
|
|
85
90
|
const payment = await hook; // [!code highlight]
|
|
86
91
|
return { success: true, payment };
|
|
87
92
|
} catch (error) {
|
|
88
|
-
if (
|
|
93
|
+
if (HookConflictError.is(error)) { // [!code highlight]
|
|
89
94
|
// Another workflow is already processing this order
|
|
95
|
+
console.log(`Conflicting token: ${error.token}`);
|
|
90
96
|
return { success: false, reason: "duplicate-processing" };
|
|
91
97
|
}
|
|
92
98
|
throw error; // Re-throw other errors
|
package/docs/errors/index.mdx
CHANGED
|
@@ -37,6 +37,12 @@ Fix common mistakes when creating and executing workflows in the **Workflow DevK
|
|
|
37
37
|
<Card href="/docs/errors/corrupted-event-log" title="corrupted-event-log">
|
|
38
38
|
Learn how to handle corrupted or invalid event logs.
|
|
39
39
|
</Card>
|
|
40
|
+
<Card href="/docs/errors/step-not-registered" title="step-not-registered">
|
|
41
|
+
Resolve step not registered errors caused by deployment mismatches.
|
|
42
|
+
</Card>
|
|
43
|
+
<Card href="/docs/errors/workflow-not-registered" title="workflow-not-registered">
|
|
44
|
+
Resolve workflow not registered errors caused by deployment mismatches.
|
|
45
|
+
</Card>
|
|
40
46
|
</Cards>
|
|
41
47
|
|
|
42
48
|
## Learn More
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: step-not-registered
|
|
3
|
+
description: A step function is not registered in the current deployment.
|
|
4
|
+
type: troubleshooting
|
|
5
|
+
summary: Resolve step not registered errors caused by build issues.
|
|
6
|
+
prerequisites:
|
|
7
|
+
- /docs/foundations/steps
|
|
8
|
+
related:
|
|
9
|
+
- /docs/errors/workflow-not-registered
|
|
10
|
+
- /docs/api-reference/workflow-errors/step-not-registered-error
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
This error occurs when the Workflow runtime tries to execute a step function that is not registered in the current deployment. When this happens, the step fails (like a `FatalError`) and control is passed back to the workflow function, which can optionally handle the failure.
|
|
14
|
+
|
|
15
|
+
## Error Message
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Step "<stepName>" is not registered in the current deployment.
|
|
19
|
+
This usually indicates a build or bundling issue that caused the step
|
|
20
|
+
to not be included in the deployment.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Why This Happens
|
|
24
|
+
|
|
25
|
+
Workflow runs are pegged to a specific deployment, so this error is not caused by newer deployments overriding the running code. Instead, it means the step function was not included in the deployment's workflow bundle at build time.
|
|
26
|
+
|
|
27
|
+
This is an **infrastructure error**, not a user code error.
|
|
28
|
+
|
|
29
|
+
## Common Causes
|
|
30
|
+
|
|
31
|
+
### Build tooling issue
|
|
32
|
+
|
|
33
|
+
Something went wrong during the build process that caused the step function to not be included in the workflow bundle. Check your build logs for errors related to workflow bundling. Common issues include:
|
|
34
|
+
|
|
35
|
+
- The step file is missing a valid `"use step"` directive
|
|
36
|
+
- The step function is not exported from the workflow file
|
|
37
|
+
- An esbuild or SWC plugin error silently excluded the step
|
|
38
|
+
|
|
39
|
+
### Step removed from the codebase
|
|
40
|
+
|
|
41
|
+
The step function was deleted or its `"use step"` directive was removed, but the workflow still references it. Ensure all steps referenced by your workflow are present in the codebase.
|
|
42
|
+
|
|
43
|
+
## How to Resolve
|
|
44
|
+
|
|
45
|
+
1. **Check your build logs:** Look for errors or warnings related to workflow bundling. Ensure the step file contains a valid `"use step"` directive and is properly exported.
|
|
46
|
+
|
|
47
|
+
2. **Verify the step exists:** Confirm the step function is present in the workflow file and has the `"use step"` directive.
|
|
48
|
+
|
|
49
|
+
3. **Handle it in your workflow:** Since the step fails like a `FatalError`, you can catch it in your workflow code:
|
|
50
|
+
|
|
51
|
+
```typescript lineNumbers
|
|
52
|
+
declare function processPayment(orderId: string): Promise<any>; // @setup
|
|
53
|
+
|
|
54
|
+
export async function myWorkflow() {
|
|
55
|
+
"use workflow";
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const result = await processPayment("order-123");
|
|
59
|
+
return { success: true, result };
|
|
60
|
+
} catch (error) {
|
|
61
|
+
// Step failure (including not registered) is caught here
|
|
62
|
+
console.error("Step failed:", error);
|
|
63
|
+
return { success: false, error: String(error) };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
@@ -43,6 +43,8 @@ export async function webhookWorkflow() {
|
|
|
43
43
|
**Solution:** Use `"manual"` or provide a `Response` object.
|
|
44
44
|
|
|
45
45
|
```typescript lineNumbers
|
|
46
|
+
import { createWebhook } from "workflow";
|
|
47
|
+
|
|
46
48
|
// Fixed - use "manual"
|
|
47
49
|
export async function webhookWorkflow() {
|
|
48
50
|
"use workflow";
|
|
@@ -74,6 +76,8 @@ export async function webhookWorkflow() {
|
|
|
74
76
|
**Solution:** Create a proper `Response` object.
|
|
75
77
|
|
|
76
78
|
```typescript lineNumbers
|
|
79
|
+
import { createWebhook } from "workflow";
|
|
80
|
+
|
|
77
81
|
// Fixed - use Response constructor
|
|
78
82
|
export async function webhookWorkflow() {
|
|
79
83
|
"use workflow";
|
|
@@ -89,6 +93,8 @@ export async function webhookWorkflow() {
|
|
|
89
93
|
### Default Behavior (202 Response)
|
|
90
94
|
|
|
91
95
|
```typescript lineNumbers
|
|
96
|
+
import { createWebhook } from "workflow";
|
|
97
|
+
|
|
92
98
|
// Returns 202 Accepted automatically
|
|
93
99
|
const webhook = await createWebhook();
|
|
94
100
|
const request = await webhook;
|
|
@@ -98,6 +104,8 @@ const request = await webhook;
|
|
|
98
104
|
### Manual Response
|
|
99
105
|
|
|
100
106
|
```typescript lineNumbers
|
|
107
|
+
import { createWebhook } from "workflow";
|
|
108
|
+
|
|
101
109
|
// Manual response control
|
|
102
110
|
const webhook = await createWebhook({
|
|
103
111
|
respondWith: "manual",
|
|
@@ -120,6 +128,8 @@ await request.respondWith(
|
|
|
120
128
|
### Pre-defined Response
|
|
121
129
|
|
|
122
130
|
```typescript lineNumbers
|
|
131
|
+
import { createWebhook } from "workflow";
|
|
132
|
+
|
|
123
133
|
// Immediate response
|
|
124
134
|
const webhook = await createWebhook({
|
|
125
135
|
respondWith: new Response("Request received", { status: 200 }),
|
|
@@ -49,6 +49,8 @@ export async function webhookWorkflow() {
|
|
|
49
49
|
**Solution:** Always call `request.respondWith()` when using manual response mode.
|
|
50
50
|
|
|
51
51
|
```typescript lineNumbers
|
|
52
|
+
import { createWebhook } from "workflow";
|
|
53
|
+
|
|
52
54
|
// Fixed - response sent
|
|
53
55
|
export async function webhookWorkflow() {
|
|
54
56
|
"use workflow";
|
|
@@ -92,6 +94,8 @@ export async function webhookWorkflow() {
|
|
|
92
94
|
**Solution:** Ensure all code paths send a response.
|
|
93
95
|
|
|
94
96
|
```typescript lineNumbers
|
|
97
|
+
import { createWebhook } from "workflow";
|
|
98
|
+
|
|
95
99
|
// Fixed - response sent in all branches
|
|
96
100
|
export async function webhookWorkflow() {
|
|
97
101
|
"use workflow";
|
|
@@ -135,6 +139,8 @@ export async function webhookWorkflow() {
|
|
|
135
139
|
**Solution:** Use try-catch to handle errors and send appropriate responses.
|
|
136
140
|
|
|
137
141
|
```typescript lineNumbers
|
|
142
|
+
import { createWebhook } from "workflow";
|
|
143
|
+
|
|
138
144
|
// Fixed - error handling with response
|
|
139
145
|
export async function webhookWorkflow() {
|
|
140
146
|
"use workflow";
|
|
@@ -163,6 +169,8 @@ export async function webhookWorkflow() {
|
|
|
163
169
|
If you don't need custom response control, consider using the default response mode which automatically returns a `202 Accepted` response:
|
|
164
170
|
|
|
165
171
|
```typescript lineNumbers
|
|
172
|
+
import { createWebhook } from "workflow";
|
|
173
|
+
|
|
166
174
|
// Automatic 202 response - no manual response needed
|
|
167
175
|
export async function webhookWorkflow() {
|
|
168
176
|
"use workflow";
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: workflow-not-registered
|
|
3
|
+
description: A workflow function is not registered in the current deployment.
|
|
4
|
+
type: troubleshooting
|
|
5
|
+
summary: Resolve workflow not registered errors caused by deployment targeting or build issues.
|
|
6
|
+
prerequisites:
|
|
7
|
+
- /docs/foundations/starting-workflows
|
|
8
|
+
related:
|
|
9
|
+
- /docs/errors/step-not-registered
|
|
10
|
+
- /docs/api-reference/workflow-errors/workflow-not-registered-error
|
|
11
|
+
---
|
|
12
|
+
|
|
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
|
+
|
|
15
|
+
## Error Message
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
Workflow "<workflowName>" is not registered in the current deployment.
|
|
19
|
+
This usually means a run was started against a deployment that does not
|
|
20
|
+
have this workflow, or there was a build/bundling issue.
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Why This Happens
|
|
24
|
+
|
|
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
|
+
|
|
27
|
+
## Common Causes
|
|
28
|
+
|
|
29
|
+
### Run started against a deployment without the workflow
|
|
30
|
+
|
|
31
|
+
A run was started (or restarted from the dashboard UI) targeting a deployment where the workflow was renamed, moved to a different file, or removed entirely.
|
|
32
|
+
|
|
33
|
+
{/* @skip-typecheck: incomplete code sample */}
|
|
34
|
+
```typescript lineNumbers title="workflows/order.ts (original)"
|
|
35
|
+
export async function processOrder(orderId: string) {
|
|
36
|
+
"use workflow";
|
|
37
|
+
// workflow logic
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
{/* @skip-typecheck: incomplete code sample */}
|
|
42
|
+
```typescript lineNumbers title="workflows/order.ts (current deployment)"
|
|
43
|
+
// Renamed from processOrder to handleOrder
|
|
44
|
+
export async function handleOrder(orderId: string) { // [!code highlight]
|
|
45
|
+
"use workflow";
|
|
46
|
+
// workflow logic
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
If a new run is started targeting the current deployment using the old name `processOrder`, the runtime will not find it.
|
|
51
|
+
|
|
52
|
+
### Build tooling issue
|
|
53
|
+
|
|
54
|
+
Something went wrong during the build process that caused the workflow function to not be included in the workflow bundle. Check your build logs for errors related to workflow bundling. Common issues include:
|
|
55
|
+
|
|
56
|
+
- The workflow file is missing a valid `"use workflow"` directive
|
|
57
|
+
- The workflow function is not exported from the workflow file
|
|
58
|
+
- An esbuild or SWC plugin error silently excluded the workflow
|
|
59
|
+
|
|
60
|
+
## How to Resolve
|
|
61
|
+
|
|
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
|
+
|
|
64
|
+
2. **If it's a build issue:** Check your build logs for errors related to workflow bundling. Ensure the workflow file contains a valid `"use workflow"` directive and is properly exported.
|
|
@@ -252,6 +252,10 @@ export async function processOrder(orderId: string) {
|
|
|
252
252
|
|
|
253
253
|
With background execution, the parent workflow continues immediately after starting the child. The child workflow runs independently with its own event log and can be monitored separately using the returned `runId`.
|
|
254
254
|
|
|
255
|
+
<Callout type="info">
|
|
256
|
+
If you want the child workflow to run on the latest deployment rather than the current one, you can pass [`deploymentId: "latest"`](/docs/api-reference/workflow-api/start#using-deploymentid-latest) in the `start()` options. This is currently a Vercel-specific feature. Be aware that the child workflow's function name, file path, argument types, and return type must remain compatible across deployments — renaming the function or changing its location will change the workflow ID, and modifying expected inputs or outputs can cause serialization failures.
|
|
257
|
+
</Callout>
|
|
258
|
+
|
|
255
259
|
**Choose direct await when:**
|
|
256
260
|
- The parent needs the child's result before continuing
|
|
257
261
|
- You want a single, unified event log
|
|
@@ -139,6 +139,35 @@ 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
|
+
## Error Codes
|
|
143
|
+
|
|
144
|
+
When a workflow run fails, the error may include a `code` that classifies the failure. You can access it programmatically via the `Run` class:
|
|
145
|
+
|
|
146
|
+
```typescript lineNumbers
|
|
147
|
+
import { WorkflowRunFailedError } from "@workflow/errors";
|
|
148
|
+
import { start } from "workflow/api";
|
|
149
|
+
|
|
150
|
+
const run = await start(myWorkflow, [input]);
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const result = await run.returnValue;
|
|
154
|
+
} catch (err) {
|
|
155
|
+
if (WorkflowRunFailedError.is(err)) {
|
|
156
|
+
console.log(err.cause.code); // "USER_ERROR", "RUNTIME_ERROR", or undefined
|
|
157
|
+
console.log(err.cause.message); // The error message
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
| Code | Meaning |
|
|
163
|
+
| --- | --- |
|
|
164
|
+
| `USER_ERROR` | An error thrown in your workflow or step code (including propagated step failures like `FatalError`) |
|
|
165
|
+
| `RUNTIME_ERROR` | An internal runtime error such as a corrupted event log or missing data. If you see this, please [file an issue](https://github.com/vercel/workflow/issues) |
|
|
166
|
+
|
|
167
|
+
<Callout type="info">
|
|
168
|
+
The error code is also available on the run entity via the CLI (`npx workflow inspect runs <runId>`) in the `error.code` field, and as an OTEL span attribute (`workflow.error.code`) for observability.
|
|
169
|
+
</Callout>
|
|
170
|
+
|
|
142
171
|
## Rolling Back Failed Steps
|
|
143
172
|
|
|
144
173
|
When a workflow fails partway through, it can leave the system in an inconsistent state.
|