| name | cloudflare-workflows |
| description | Cloudflare Workflows for durable long-running execution. Use for multi-step workflows, retries, state persistence, or encountering NonRetryableError, execution failed errors. |
| license | MIT |
| metadata | {"version":"3.0.0","wrangler_version":"4.81.0","workers_types_version":"4.20260408.0","last_verified":"2025-12-27","errors_prevented":5,"templates_included":8,"references_included":8,"agents_included":3,"commands_included":4,"scripts_included":5,"keywords":["cloudflare workflows","workflows workers","durable execution","workflow step","WorkflowEntrypoint","step.do","step.sleep","workflow retries","NonRetryableError","workflow state","wrangler workflows","workflow events","long-running tasks","step.sleepUntil","step.waitForEvent","workflow bindings"]} |
Cloudflare Workflows
Status: Production Ready ✅ | Last Verified: 2025-12-27 | Version: 3.0.0
Dependencies: cloudflare-worker-base (for Worker setup)
Contents: Quick Start • Commands • Agents • Core Concepts • Critical Rules • Top Errors • Common Patterns • When to Load References • Limits
Quick Start (10 Minutes)
1. Create a Workflow
Use the Cloudflare Workflows starter template:
npm create cloudflare@latest my-workflow -- --template cloudflare/workflows-starter --git --deploy false
cd my-workflow
What you get:
- WorkflowEntrypoint class template
- Worker to trigger workflows
- Complete wrangler.jsonc configuration
2. Basic Workflow Structure
src/index.ts:
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
type Env = {
MY_WORKFLOW: Workflow;
};
type Params = {
userId: string;
email: string;
};
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
const { userId, email } = event.payload;
const result = await step.do('process user', async () => {
return { processed: true, userId };
});
await step.sleep('wait 1 hour', '1 hour');
await step.do('send email', () => {
{ : , email };
});
{ : , userId };
}
}
{
(: , : ): <> {
instance = env..({
: { : , : }
});
.({
: instance.,
: instance.()
});
}
};
Template: See templates/basic-workflow.ts for complete example
3. Configure wrangler.jsonc
{
"name": "my-workflow",
"main": "src/index.ts",
"compatibility_date": "2025-10-22",
"workflows": [
{
"binding": "MY_WORKFLOW",
"name": "my-workflow",
"class_name": "MyWorkflow"
}
]
}
Template: See templates/wrangler-workflows-config.jsonc
4. Deploy
npm run deploy
Commands
Interactive slash commands for workflow development:
| Command | Description | Use When |
|---|
/workflow-setup | Complete wizard for new workflow projects | Starting new project, need full setup |
/workflow-create | Quick scaffolding for workflow classes | Adding workflow to existing project |
/workflow-debug | Interactive debugging with error patterns | Troubleshooting workflow issues |
/workflow-test | Test workflows locally and remotely | Validating workflow behavior |
Example Usage:
/workflow-setup # Full guided setup wizard
/workflow-create # Quick workflow scaffolding
/workflow-debug # Debug workflow issues
/workflow-test # Test workflow execution
Agents
Autonomous agents for complex workflow tasks:
| Agent | Description | Triggers |
|---|
workflow-debugger | Auto-detects and fixes configuration/runtime errors | "debug workflow", "fix workflow errors" |
workflow-optimizer | Analyzes performance, cost, and reliability | "optimize workflow", "improve performance" |
workflow-setup-assistant | Autonomous project scaffolding | "setup workflow", "create first workflow" |
Key Capabilities:
- Debugger: 6-phase analysis, auto-fix for I/O context, serialization, export issues
- Optimizer: Cost analysis, reliability scoring, actionable recommendations
- Setup Assistant: Project detection, automatic scaffolding, validation
Scripts
Automation scripts in scripts/ directory:
| Script | Purpose |
|---|
validate-workflow-config.sh | Validate wrangler.jsonc configuration |
test-workflow.sh | Create and test workflow instances |
benchmark-workflow.sh | Measure performance and cost |
generate-workflow.sh | Scaffold new workflows from templates |
check-workflow-limits.sh | Validate against Cloudflare limits |
Usage:
./scripts/validate-workflow-config.sh
./scripts/test-workflow.sh my-workflow
./scripts/benchmark-workflow.sh my-workflow 10
./scripts/generate-workflow.sh MyWorkflow
./scripts/check-workflow-limits.sh src/workflows/my-workflow.ts
Core Concepts
WorkflowEntrypoint
Every workflow must extend WorkflowEntrypoint:
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
}
}
Key Points:
Env: Environment bindings (KV, D1, etc.)
Params: Typed payload passed when creating workflow instance
event: Contains id, payload, timestamp
step: Methods for durable execution
Step Methods
All workflow work MUST be done in steps for durability:
await step.do('step name', async () => {
return { result: 'data' };
});
await step.sleep('wait', '1 hour');
await step.sleepUntil('wait until', Date.now() + 3600000);
const event = await step.waitForEvent('payment received', 'payment.completed', {
timeout: '30 minutes'
});
CRITICAL: All I/O (fetch, KV, D1, R2) must happen inside step.do() callbacks!
Reference: See references/workflow-patterns.md for all patterns
Critical Rules
Always Do ✅
✅ Perform all I/O inside step.do() - Required for durability
✅ Use named steps - Makes debugging easier
✅ Return JSON-serializable data from steps - Required for state persistence
✅ Use step.sleep() for delays - Don't use setTimeout()
✅ Handle errors explicitly - Use try/catch in step callbacks
✅ Use NonRetryableError for permanent failures - Stops retries
Workflow Patterns: See references/workflow-patterns.md for:
- Sequential workflows
- Parallel execution
- Event-driven workflows
- Scheduled workflows
- Human-in-the-loop workflows
Never Do ❌
❌ Never do I/O outside step.do() - Will fail with "I/O context" error
❌ Never use setTimeout() or setInterval() - Use step.sleep() instead
❌ Never return non-serializable data - Functions, Promises, etc. will fail
❌ Never hardcode timeouts - Use workflow config
❌ Never ignore NonRetryableError - Indicates permanent failure
Top 5 Critical Errors
Error #1: I/O Context Error ⚠️
Error:
Cannot perform I/O on behalf of a different request
Cause: Performing I/O outside step.do() callback
Solution:
const data = await fetch('https://api.example.com');
await step.do('use data', async () => {
return data;
});
const data = await step.do('fetch data', async () => {
const response = await fetch('https://api.example.com');
return await response.json();
});
Error #2: Serialization Error
Error:
Cannot serialize workflow state
Cause: Returning non-JSON-serializable data from step
Solution:
await step.do('process', async () => {
return { fn: () => {} };
});
await step.do('process', async () => {
return { result: 'data' };
});
Error #3: NonRetryableError Not Thrown
Error: Workflow retries forever on permanent failures
Solution:
import { NonRetryableError } from 'cloudflare:workers';
await step.do('validate', async () => {
if (!isValid) {
throw new NonRetryableError('Invalid input');
}
return { valid: true };
});
Error #4: WorkflowEvent Not Found
Error:
WorkflowEvent 'payment.completed' not found
Cause: Event name mismatch between waitForEvent and trigger
Solution:
const event = await step.waitForEvent('wait payment', 'payment.completed', {
timeout: '30 minutes'
});
await instance.trigger('payment.completed', { amount: 100 });
Error #5: Workflow Execution Failed
Error:
Workflow execution failed: Step timeout exceeded
Cause: Step exceeds maximum CPU time (30 seconds)
Solution:
await step.do('long task', async () => {
for (let i = 0; i < 1000000; i++) {
}
});
for (let i = 0; i < 100; i++) {
await step.do(`batch ${i}`, async () => {
});
}
All Issues: See references/common-issues.md for complete documentation
Common Patterns
Sequential Workflow
Basic workflow with steps executing in order. Each step completes before the next begins.
Use cases: Order processing, user onboarding, data pipelines
Load templates/basic-workflow.ts for complete example
Scheduled Workflow
Workflow with time delays between steps using step.sleep() or step.sleepUntil().
Use cases: Reminder sequences, scheduled tasks, delayed notifications
Load templates/scheduled-workflow.ts for complete example
Event-Driven Workflow
Wait for external events with step.waitForEvent(). Always set timeout and handle with NonRetryableError:
const payment = await step.waitForEvent('wait payment', 'payment.completed', {
timeout: '30 minutes'
});
if (!payment) throw new NonRetryableError('Payment timeout');
Load templates/workflow-with-events.ts for complete example
Workflow with Retries
Use NonRetryableError for permanent failures (404), regular Error for transient failures (5xx):
const data = await step.do('fetch', async () => {
const response = await fetch(url);
if (!response.ok) {
if (response.status === 404) throw new NonRetryableError('Not found');
throw new Error('Temporary failure');
}
return await response.json();
});
Load templates/workflow-with-retries.ts for complete example with retry configuration
Triggering Workflows
From Worker: Create instances via env.MY_WORKFLOW.create(), get status with instance.status(), trigger events with instance.trigger().
From Cron: Use scheduled() handler to create workflow instances on schedule.
Load templates/worker-trigger.ts for complete Worker trigger example
Load templates/scheduled-workflow.ts for complete Cron trigger example
When to Load References
references/common-issues.md: Encountering I/O context, serialization, NonRetryableError, event naming, or timeout errors; troubleshooting workflow failures.
references/workflow-patterns.md: Building complex orchestration, approval workflows, idempotency patterns, or circuit breaker patterns.
references/wrangler-commands.md: Need CLI commands for managing workflow instances, debugging stuck workflows, or monitoring production.
references/production-checklist.md: Preparing for deployment, need pre-deployment verification, setting up monitoring/error handling.
references/limits-quotas.md: Hitting instance/step/payload limits, optimizing for cost, designing high-volume workflows.
references/2025-features.md: Using events system, enhanced retries, instance lifecycle control, or latest Workflows features.
references/metrics-analytics.md: Setting up monitoring, custom metrics, external logging integration, or workflow dashboards.
references/troubleshooting.md: Complex debugging scenarios, stuck instances, systematic diagnosis, performance issues.
templates/: basic-workflow.ts (sequential), scheduled-workflow.ts (delays/sleep), workflow-with-events.ts (waitForEvent), workflow-with-retries.ts (custom retry), worker-trigger.ts (Worker triggers), wrangler-workflows-config.jsonc (Wrangler config), parallel-execution-workflow.ts (batched parallel processing), circuit-breaker-workflow.ts (resilient external calls)
Wrangler Commands
Key Commands: wrangler workflows create, wrangler workflows instances list/describe/terminate, wrangler deploy
Load references/wrangler-commands.md for complete CLI reference with all workflow management commands, monitoring workflows, and debugging stuck instances.
State Persistence
Workflows automatically persist state between steps. No manual state management needed:
export class StatefulWorkflow extends WorkflowEntrypoint {
async run(event, step) {
const result1 = await step.do('step 1', async () => {
return { data: 'value' };
});
await step.sleep('wait', '1 hour');
await step.do('step 2', async () => {
console.log(result1.data);
});
}
}
Key Points:
- Step results automatically persisted
- Completed steps never re-run (even after crash/restart)
- State available throughout workflow lifetime
Limits
| Resource | Limit |
|---|
| Step CPU Time | 30 seconds |
| Workflow Duration | 30 days |
| Step Payload Size | 128 KB |
| Workflow Payload Size | 128 KB |
| Steps per Workflow | 1,000 |
| Concurrent Instances | 1,000 per workflow |
| Event Payload Size | 128 KB |
Workarounds:
- Large data: Store in KV/R2, pass key in step
- Long CPU: Break into smaller steps
- Many steps: Consider sub-workflows
Pricing
- Duration: $0.02 per million GB-s (same as Workers)
- Requests: $0.15 per million (workflow creation + step execution)
- State Storage: Included (no additional cost)
- Sleep: Free (no CPU usage during sleep)
Example Cost (1M workflow runs):
- 5 steps each = 5M requests = $0.75
- 10ms per step = 50GB-s = $0.001
- Total: ~$0.75 per million workflows
Troubleshooting
"I/O context" error
Solution: Move all I/O into step.do() callbacks → See references/common-issues.md #1
"Serialization error"
Solution: Return only JSON-serializable data from steps → See references/common-issues.md #2
Workflow retries forever
Solution: Throw NonRetryableError for permanent failures → See references/common-issues.md #3
"WorkflowEvent not found"
Solution: Ensure event names match exactly → See references/common-issues.md #4
"Step timeout exceeded"
Solution: Break long computations into smaller steps → See references/common-issues.md #5
Production Checklist
10-Point Pre-Deployment Checklist: I/O context isolation, JSON serialization, NonRetryableError usage, event name consistency, step duration limits, error handling, retry configuration, timeouts, workflow naming, and monitoring.
Load references/production-checklist.md for complete checklist with detailed explanations, code examples, verification steps, and deployment workflow.
Official Documentation
Workflows: https://developers.cloudflare.com/workflows/ • API Reference: https://developers.cloudflare.com/workflows/reference/ • Examples: https://developers.cloudflare.com/workflows/examples/ • Blog: https://blog.cloudflare.com/cloudflare-workflows/