Troubleshooting
Diagnose deploy, bootstrap, GitHub, sandbox, queue, and publication failures.
Start from the durable workflow-run or work-item record, then correlate its identifiers with wrangler tail output and Workflow instance status — see Observability.
Deploy fails or the Worker won't start
wrangler deployfails during provisioning — the target Cloudflare account is not on the Workers Paid plan; Containers, Durable Objects, Workflows, Queues, D1, and R2 all require it. See Requirements.TASUKU_MASTER_KEY must be a base64-encoded 32-byte key— generate it withopenssl rand -base64 32and preserve it; see Environment variables.TASUKU_MASTER_KEY_VERSION must be a positive integer— use an integer of at least1.TASUKU_MAX_CONCURRENT_AGENTS/TASUKU_MAX_CONCURRENT_REVIEWS/TASUKU_GLOBAL_MAX_RUNNING must be a positive integer— use an integer of at least1.invalid environment configuration:\n- Invalid option: expected one of "development"|"test"|"production"—TASUKU_ENV's zod default message (server/src/env.ts); correct the value.bun run check:pinsfails — the runtime image tag disagrees across the five places it's pinned (runtime/package.json,wrangler.jsonc,runtime/Dockerfile.cloudflare,runtime/deploy/docker-compose.external.yml,runtime/src/version.ts); bump all five together. See Deployment.
Container image pull is slow
The default containers[].image is a docker.io reference, and Cloudflare does not cache externally-referenced images in your account's registry — every cold start re-pulls it. If cold starts are consistently slow, run bun run deploy --cached once (requires local Docker) to push the image into your account's registry, where Cloudflare does cache it. See Install Tasuku.
Runtime returns 426
The Worker sent X-Tasuku-Protocol: <major> and the agent runtime container responded 426, meaning its tasuku-runtime build speaks a different major protocol version than the deployed Worker expects. This means the pinned runtime image and the deployed Worker's packages/protocol version have drifted apart — redeploy with the image version wrangler.jsonc expects, or update the pin and redeploy both together (bun run check:pins).
A queue's dead-letter queue is filling up
There are three dead-letter queues, one per source queue: tasuku-inbox-dlq (tasuku-inbox), tasuku-effects-dlq (tasuku-effects), and tasuku-review-batches-dlq (tasuku-review-batches). Messages landing in any of them means a Slack/Linear inbox item, an outbound GitHub/Slack/Linear effect, or a review batch has failed 5 times (max_retries) without succeeding. Inspect the DLQ message body for the underlying error — a permanent GitHub/Slack/Linear API rejection (bad credentials, revoked scope, deleted resource) won't resolve on its own; a transient one usually already retried through before landing here. See Observability.
GitHub setup or signup fails
INVALID_GITHUB_APP_CONFIGURATION— repeat the manifest conversion with the expected fields and matching public URL.GITHUB_APP_IDENTITY_UNAVAILABLE— the App identity has not been configured or cannot be resolved.GITHUB_EMAIL_PERMISSION_REQUIRED— add Email addresses: read to the App and reauthorize it.- New signup is rejected — check
TASUKU_AUTO_SIGNUP_DOMAINSfor an exact verified-email domain; parent domains do not include subdomains.
Repository receives no work
Confirm that the repository is still selected in the GitHub installation, synchronized into Tasuku, explicitly enabled, and eligible under the event, draft, author, path, and changed-file policy. Then check GitHub webhook delivery and Tasuku's delivery deduplication record.
Codex device auth works but chatgpt.com egress fails
These are two different network paths and can fail independently. Device auth and token refresh talk to auth.openai.com/api.openai.com, which are reachable from Worker fetch() — device auth keeps working even when the second path is blocked. Codex inference talks to chatgpt.com, which is blocked from Worker-mediated egress (Cloudflare's Cf-Worker header trips its block page), and @cloudflare/containers has no per-host bypass for its outbound-Worker interception once it's engaged. That makes Codex unsupported on the CLOUDFLARE_CONTAINER sandbox provider entirely — not merely slow or occasionally blocked — and Tasuku fails fast rather than let a run reach the sandbox and silently fail partway through: a non-retryable PrepareError at run-prepare time, and a BLOCKING sandbox readiness-check finding, both with the identical message. Run Codex on DAYTONA or EXTERNAL_RUNTIME instead, where credentials are INLINE and egress was never intercepted by a Cloudflare Outbound Worker in the first place — chatgpt.com reaches those sandboxes directly, still bounded by that provider's own egress policy (STRICT/BUILD/DEEP_SCAN allowlist, or the Daytona/external firewall) — see Security.
Configuration will not save
- Cloudflare Container sandbox config rejects unknown fields — the decoder is strict; check field names against Sandbox providers.
- Daytona's config has no
api_keyfield at all — the key always comes from the${secrets.DAYTONA_API_KEY}reference at the sandbox profile's own scope, never from the config JSON.api_urlis required (no schema-level default; the dashboard form pre-fillshttps://app.daytona.io/api) and must resolve to Daytona's own API host. - An external runtime host requires an HTTPS
base_url, anauth_header_templatesecret reference, and a positive integertimeout_minutes(also required, no default) — no userinfo or IP-literal hosts. - MCP URLs must use HTTPS and transports must be
HTTPorSSE. - Review strictness must be
LOW,MEDIUM, orHIGH. - Changed-file limit must be
1–5000; comment limit must be1–100.
Task repeatedly fails
Inspect attempt history (RunAttemptWorkflow instance status) to identify the stage:
- repository token or clone (
prepare); - sandbox creation (
acquire-sandbox); - Codex, Claude, or OpenCode authentication and model selection;
- MCP or tool access;
- output parsing or artifact validation (
publish); - GitHub publication.
Transient failures retry with backoff inside the Workflow. A repeating deterministic error needs configuration, runtime, or repository correction — retrying alone reproduces it.
Review shows a red check with a capacity error
Daytona returns "Total memory limit exceeded" (or a CPU, disk, or concurrency limit) when the
sandboxes already running under your account fill your Daytona organization's tier. That ceiling is
independent of TASUKU_MAX_CONCURRENT_REVIEWS and TASUKU_MAX_CONCURRENT_AGENTS: each concurrent
run provisions a sandbox at the profile's memory_gb, so three concurrent reviews at memory_gb: 4
need 12 GiB of headroom, more than a 10 GiB tier allows. Size the profile's memory_gb and Tasuku's
concurrency settings against the provider's real capacity, or raise the Daytona tier. On
CLOUDFLARE_CONTAINER the equivalent ceiling is containers[].max_instances in wrangler.jsonc
(mirrored by TASUKU_CONTAINER_STANDARD_MAX and TASUKU_CONTAINER_LARGE_MAX); an external runtime
reports it as active_runs >= max_runs.
Tasuku treats these responses as capacity failures rather than ordinary errors: the job is requeued
about every 60 seconds for up to 30 minutes after the run was created, and those retries do not
spend the job's max_attempts budget, so a review that was merely waiting for another sandbox to
finish recovers on its own. Past 30 minutes the failure counts like any other retryable error.
The GitHub check run reflects the outcome honestly: a run that ends DEAD completes with conclusion
failure and a summary that names the error, and a cancelled run completes as cancelled. Only a
run that actually published its review completes as success.
Sandbox lost mid-run
If the agent runtime container dies mid-attempt (host restart, sleepAfter eviction, OOM), the next GET /v1/runs/:id/events call gets a 404 from the runtime, which the Workflow turns into FAILED (retryable): SANDBOX_LOST. A new attempt is the recovery — the same behavior as a lease expiring under the previous lease-based worker.
Migration state problems
Migrations apply in order from server/drizzle/, tracked in D1's d1_migrations table by both the Migrator Durable Object and wrangler d1 migrations apply. A fresh database applies the full sequence starting at 0000_initial.sql. Partially migrated or hand-edited databases are not supported — restore from a Time Travel bookmark or a clean export instead of editing d1_migrations by hand.
Restored secrets no longer work
Verify that the restored TASUKU_MASTER_KEY exactly matches the key used when the database was sealed. A new key with the same version number is still a different key.