# Gotchas & Debugging ## Common Errors ### "Step Timeout" **Cause:** Step execution exceeding 10 minute default timeout or configured timeout **Solution:** Set custom timeout with `step.do('long operation', {timeout: '30 minutes'}, async () => {...})` or increase CPU limit in wrangler.jsonc (max 5min CPU time) ### "waitForEvent Timeout" **Cause:** Event not received within timeout period (default 24h, max 365d) **Solution:** Wrap in try-catch to handle timeout gracefully and proceed with default behavior ### "Non-Deterministic Step Names" **Cause:** Using dynamic values like `Date.now()` in step names causes replay issues **Solution:** Use deterministic values like `event.instanceId` for step names ### "State Lost in Variables" **Cause:** Using module-level or local variables to store state which is lost on hibernation **Solution:** Return values from `step.do()` which are automatically persisted: `const total = await step.do('step 1', async () => 10)` ### "Non-Deterministic Conditionals" **Cause:** Using non-deterministic logic (like `Date.now()`) outside steps in conditionals **Solution:** Move non-deterministic operations inside steps: `const isLate = await step.do('check', async () => Date.now() > deadline)` ### "Large Step Returns Exceeding Limit" **Cause:** Returning data >1 MiB from step **Solution:** Store large data in R2 and return only reference: `{ key: 'r2-object-key' }` ### "Step Exceeded CPU Limit But Ran for < 30s" **Cause:** Confusion between CPU time (active compute) and wall-clock time (includes I/O waits) **Solution:** Network requests, database queries, and sleeps don't count toward CPU. 30s limit = 30s of active processing ### "Idempotency Violation" **Cause:** Step operations not idempotent, causing duplicate charges or actions on retry **Solution:** Check if operation already completed before executing (e.g., check if customer already charged) ### "Instance ID Collision" **Cause:** Reusing instance IDs causing conflicts **Solution:** Use unique IDs with timestamp: `await env.MY_WORKFLOW.create({ id: \`${userId}-${Date.now()}\`, params: {} })` ### "Instance Data Disappeared After Completion" **Cause:** Completed/errored instances are automatically deleted after retention period (3 days free / 30 days paid) **Solution:** Export critical data to KV/R2/D1 before workflow completes ### "Missing await on step.do" **Cause:** Forgetting to await step.do() causing fire-and-forget behavior **Solution:** Always await step operations: `await step.do('task', ...)` ## Limits | Limit | Free | Paid | Notes | |-------|------|------|-------| | CPU per step | 10ms | 30s (default), 5min (max) | Set via `limits.cpu_ms` in wrangler.jsonc | | Step state | 1 MiB | 1 MiB | Per step return value | | Instance state | 100 MB | 1 GB | Total state per workflow instance | | Steps per workflow | 1,024 | 1,024 | `step.sleep()` doesn't count | | Executions per day | 100k | Unlimited | Daily execution limit | | Concurrent instances | 25 | 10k | Maximum concurrent workflows; waiting state excluded | | Queued instances | 100k | 1M | Maximum queued workflow instances | | Subrequests per step | 50 | 1,000 | Maximum outbound requests per step | | State retention | 3 days | 30 days | How long completed instances kept | | Step timeout default | 10 min | 10 min | Per attempt | | waitForEvent timeout default | 24h | 24h | Maximum 365 days | | waitForEvent timeout max | 365 days | 365 days | Maximum wait time | **Note:** Instances in `waiting` state (from `step.sleep` or `step.waitForEvent`) don't count toward concurrent instance limit, allowing millions of sleeping workflows. ## Pricing | Metric | Free | Paid | Notes | |--------|------|------|-------| | Requests | 100k/day | 10M/mo + $0.30/M | Workflow invocations | | CPU time | 10ms/invoke | 30M CPU-ms/mo + $0.02/M CPU-ms | Actual CPU usage | | Storage | 1 GB | 1 GB/mo + $0.20/GB-mo | All instances (running/errored/sleeping/completed) | ## References - [Official Docs](https://developers.cloudflare.com/workflows/) - [Get Started Guide](https://developers.cloudflare.com/workflows/get-started/guide/) - [Workers API](https://developers.cloudflare.com/workflows/build/workers-api/) - [REST API](https://developers.cloudflare.com/api/resources/workflows/) - [Examples](https://developers.cloudflare.com/workflows/examples/) - [Limits](https://developers.cloudflare.com/workflows/reference/limits/) - [Pricing](https://developers.cloudflare.com/workflows/reference/pricing/) See: [README.md](./README.md), [configuration.md](./configuration.md), [api.md](./api.md), [patterns.md](./patterns.md)