Tasuku
Operations

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 deploy fails 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 with openssl rand -base64 32 and preserve it; see Environment variables.
  • TASUKU_MASTER_KEY_VERSION must be a positive integer — use an integer of at least 1.
  • TASUKU_MAX_CONCURRENT_AGENTS/TASUKU_MAX_CONCURRENT_REVIEWS/TASUKU_GLOBAL_MAX_RUNNING must be a positive integer — use an integer of at least 1.
  • 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:pins fails — 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_DOMAINS for 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_key field 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_url is required (no schema-level default; the dashboard form pre-fills https://app.daytona.io/api) and must resolve to Daytona's own API host.
  • An external runtime host requires an HTTPS base_url, an auth_header_template secret reference, and a positive integer timeout_minutes (also required, no default) — no userinfo or IP-literal hosts.
  • MCP URLs must use HTTPS and transports must be HTTP or SSE.
  • Review strictness must be LOW, MEDIUM, or HIGH.
  • Changed-file limit must be 1–5000; comment limit must be 1–100.

Task repeatedly fails

Inspect attempt history (RunAttemptWorkflow instance status) to identify the stage:

  1. repository token or clone (prepare);
  2. sandbox creation (acquire-sandbox);
  3. Codex, Claude, or OpenCode authentication and model selection;
  4. MCP or tool access;
  5. output parsing or artifact validation (publish);
  6. 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.

On this page