workflow
Version:
Workflow SDK - Build durable, resilient, and observable workflows
161 lines (116 loc) • 5.68 kB
text/mdx
---
title: serialization-failed
description: Ensure all data passed between workflow and step functions is serializable.
type: troubleshooting
summary: Resolve serialization errors by passing only serializable types across execution boundaries.
prerequisites:
- /docs/foundations/serialization
related:
- /docs/foundations/workflows-and-steps
---
<CopyPrompt
text="Fix Workflow serialization errors. Find the failing `start()` call, workflow argument, step return value, hook payload, or stream chunk. Replace non-serializable values such as class instances, functions, SDK clients, Response/Request objects, streams, database connections, Dates that need custom handling, Maps/Sets, or circular objects with plain JSON-compatible data, IDs, strings, numbers, booleans, arrays, and objects. Recreate runtime-only clients or objects inside `"use step"` helpers instead of passing them through the workflow log. For external resources, pass a stable ID or URL and load the resource inside the step. Add a test or local route call that serializes the same input/output path successfully."
/>
This error occurs when you try to pass non-serializable data between execution boundaries in your workflow. All data passed between workflow functions, step functions, and the workflow runtime must be serializable to persist in the event log.
## Error message
```text
Failed to serialize workflow arguments. Ensure you're passing serializable types
(plain objects, arrays, primitives, Date, RegExp, Map, Set).
```
This error can appear when:
- Serializing workflow arguments when calling `start()`
- Serializing workflow return values
- Serializing step arguments
- Serializing step return values
## Where the error surfaces
Where you observe the failure depends on which boundary it crosses:
- **Workflow arguments**: `start()` throws synchronously in your application code.
- **Step arguments and step return values**: the *step* fails with the `SerializationError`, exactly like a step whose body threw a fatal error: no retries (the failure is deterministic), and a `try/catch` around the step call in your workflow code observes it. The step's recorded input shows `[input unavailable: step argument serialization failed]` when the arguments were the unserializable part.
- **Workflow return values**: the workflow body has already returned, so nothing can catch it; the run fails.
```typescript lineNumbers
async function stepWithBadArguments(value: unknown) {
"use step";
return value;
}
export async function processWorkflow(someValue: unknown) {
"use workflow";
try {
await stepWithBadArguments(someValue);
} catch (err) {
// err.name === "SerializationError"
// "Failed to serialize step arguments at path ..."
}
}
```
Uncaught, the error propagates out of the workflow body and the run fails immediately with the error code `USER_ERROR`; it does not retry.
## Why this happens
Workflows persist their state using an event log. Every value that crosses execution boundaries must be:
1. **Serialized** to be stored in the event log
2. **Deserialized** when the workflow resumes
Functions, class instances, symbols, and other non-serializable types cannot be properly reconstructed after serialization, which would break workflow replay.
## Common causes
### Passing functions
{/* @skip-typecheck: incomplete code sample */}
```typescript lineNumbers
// Error - functions cannot be serialized
export async function processWorkflow() {
"use workflow";
const callback = () => console.log("done"); // [!code highlight]
await processStep(callback); // Error! // [!code highlight]
}
```
**Solution:** Pass data instead, then define the function logic in the step.
```typescript lineNumbers
// Fixed - pass configuration data instead
export async function processWorkflow() {
"use workflow";
await processStep({ shouldLog: true }); // [!code highlight]
}
async function processStep(config: { shouldLog: boolean }) {
"use step";
if (config.shouldLog) { // [!code highlight]
console.log("done"); // [!code highlight]
} // [!code highlight]
}
```
### Class instances
```typescript lineNumbers
class User {
constructor(public name: string) {}
greet() { return `Hello ${this.name}`; }
}
// Error - class instances lose methods after serialization
export async function greetWorkflow() {
"use workflow";
await greetStep(new User("Alice")); // Error! // [!code highlight]
}
```
**Solution:** Pass plain objects and reconstruct the class in the step.
```typescript lineNumbers
class User {
constructor(public name: string) {}
greet() { return `Hello ${this.name}`; }
}
// Fixed - pass plain object, reconstruct in step
export async function greetWorkflow() {
"use workflow";
await greetStep({ name: "Alice" }); // [!code highlight]
}
async function greetStep(userData: { name: string }) {
"use step";
const user = new User(userData.name); // [!code highlight]
console.log(user.greet());
}
```
## Supported serializable types
Workflow SDK supports these types across execution boundaries:
### Standard JSON types
- `string`, `number`, `boolean`, `null`
- Arrays of serializable values
- Plain objects with serializable values
To learn more about supported types, see the [Serialization](/docs/foundations/serialization) section.
## Debugging serialization issues
To identify what's causing serialization to fail:
1. **Check the error stack trace**: It often shows which property failed.
2. **Simplify your data**: Temporarily pass smaller objects to isolate the issue.
3. **Use supported data types**: See [Serialization](/docs/foundations/serialization) for details.