Cloudflare Workflows
Status: Production Ready β
(GA since April 2025)
Last Updated: 2026-01-09
Dependencies: cloudflare-worker-base (for Worker setup)
Latest Versions: [email protected], @cloudflare/[email protected]
Recent Updates (2025):
- April 2025: Workflows GA release - waitForEvent API, Vitest testing, CPU time metrics, 4,500 concurrent instances
- October 2025: Instance creation rate 10x faster (100/sec), concurrency increased to 10,000
- 2025 Limits: Max steps 1,024, state persistence 1MB/step (100MB-1GB per instance), event payloads 1MB, CPU time 5 min max
- Testing: cloudflare:test module with introspectWorkflowInstance, disableSleeps, mockStepResult, mockEvent modifiers
- Platform: Waiting instances don't count toward concurrency, retention 3-30 days, subrequests 50-1,000
Quick Start (5 Minutes)
npm create cloudflare@latest my-workflow -- --template cloudflare/workflows-starter --git --deploy false
cd my-workflow
{
"name": "my-workflow",
"main": "src/index.ts",
"compatibility_date": "2025-11-25",
"workflows": [{
"name": "my-workflow",
"binding": "MY_WORKFLOW",
"class_name": "MyWorkflow"
}]
}
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from 'cloudflare:workers';
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
const result = await step.do('process', async () => { /* work */ });
await step.sleep('wait', '1 hour');
await step.do('continue', async () => { /* more work */ });
}
}
npm run deploy
npx wrangler workflows instances list my-workflow
CRITICAL: Extends WorkflowEntrypoint, implements run() with step methods, bindings in wrangler.jsonc
Known Issues Prevention
This skill prevents 12 documented errors with Cloudflare Workflows.
Issue #1: waitForEvent Skips Events After Timeout in Local Dev
Error: Events sent after a waitForEvent() timeout are ignored in subsequent waitForEvent() calls
Environment: Local development (wrangler dev) only - works correctly in production
Source: GitHub Issue #11740
Why It Happens: Bug in miniflare that was fixed in production (May 2025) but not ported to local emulator. After a timeout, the event queue becomes corrupted for that instance.
Prevention:
- Test waitForEvent timeout scenarios in production/staging, not local dev
- Avoid chaining multiple
waitForEvent() calls where timeouts are expected
Example of Bug:
export class MyWorkflow extends WorkflowEntrypoint<Env, Params> {
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
for (let i = 0; i < 3; i++) {
try {
const evt = await step.waitForEvent(`wait-${i}`, {
type: 'user-action',
timeout: '5 seconds'
});
console.log(`Iteration ${i}: Received event`);
} catch {
console.log(`Iteration ${i}: Timeout`);
}
}
}
}
Status: Known bug, fix pending for miniflare.
Issue #2: getPlatformProxy() Fails With Workflow Bindings
Error: MiniflareCoreError [ERR_RUNTIME_FAILURE]: The Workers runtime failed to start
Message: Worker's binding refers to service with named entrypoint, but service has no such entrypoint
Source: GitHub Issue #9402
Why It Happens: getPlatformProxy() from wrangler package doesn't support Workflow bindings (similar to how it handles Durable Objects). This blocks Next.js integration and local CLI scripts.
Prevention:
- Option 1: Comment out workflow bindings when using
getPlatformProxy()
- Option 2: Create separate
wrangler.cli.jsonc without workflows for CLI scripts
- Option 3: Access workflow bindings directly via deployed worker, not proxy
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-01-20"
}
import { getPlatformProxy } from 'wrangler';
const { env } = await getPlatformProxy({ configPath: './wrangler.cli.jsonc' });
Status: Known limitation, fix planned (filter workflows similar to DOs).
Issue #3: Workflow Instance Lost After Immediate Redirect (Local Dev)
Error: Instance ID returned but instance.not_found when queried
Environment: Local development (wrangler dev) only - works correctly in production
Source: GitHub Issue #10806
Why It Happens: Returning a redirect immediately after workflow.create() causes request to "soft abort" before workflow initialization completes (single-threaded execution in dev).
Prevention: Use ctx.waitUntil() to ensure workflow initialization completes before redirect:
export default {
async fetch(req: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const workflow = await env.MY_WORKFLOW.create({ params: { userId: '123' } });
ctx.waitUntil(workflow.status());
return Response.redirect('/dashboard', 302);
}
};
Status: Fixed in recent wrangler versions (post-Sept 2025), but workaround still recommended for compatibility.
Issue #4: Vitest Tests Unreliable in CI Environments
Error: [vitest-worker]: Timeout calling "resolveId"
Environment: CI/CD pipelines (GitLab, GitHub Actions) - works locally
Source: GitHub Issue #10600
Why It Happens: @cloudflare/vitest-pool-workers has resource constraint issues in CI containers, affecting workflow tests more than other worker types.
Prevention:
- Increase
testTimeout in vitest config:
export default defineWorkersConfig({
test: {
testTimeout: 60_000
}
});
- Check CI resource limits (CPU/memory)
- Use
isolatedStorage: false if not testing storage isolation
- Consider testing against deployed instances instead of vitest for critical workflows
Status: Known issue, investigating (Internal: WOR-945).
Issue #5: Instance restart() and terminate() Not Implemented in Local Dev
Error: Error: Not implemented yet when calling instance.restart() or instance.terminate()
Environment: Local development (wrangler dev) only - works in production
Source: GitHub Issue #11312
Why It Happens: Instance management APIs not yet implemented in miniflare. Additionally, instance status shows running even when workflow is sleeping.
Prevention: Test instance lifecycle management (pause/resume/terminate) in production or staging environment until local dev support is added.
const instance = await env.MY_WORKFLOW.get(instanceId);
await instance.restart();
await instance.terminate();
Status: Known limitation, no timeline for local dev support.
Is