Skip to content
Esc
↑↓navigate↵open⌘Jpreview
On this page

Troubleshooting

Diagnose failed connections, repeated cache misses, rejected uploads, and missing dashboard stats.

A green build doesn’t prove the remote cache worked. When turbo can’t reach the server, it keeps going with the local cache and prints one line about it. Find your symptom below. The commands expect your server’s URL in CACHE_URL and its token in TURBO_TOKEN.

turbo cannot connect

turbo prints the reason in place of Remote caching enabled:

Message Cause
Remote caching disabled (TURBO_TOKEN set without TURBO_TEAM) No team is set.
Remote caching unavailable (Authentication failed ...) The token doesn’t match the server’s TURBO_TOKEN.
Remote caching unavailable (Could not connect to "https://cache.example.com/") The URL is wrong or ends in a slash.

To narrow it down, check that the server is reachable and accepts your token:

curl --fail-with-body "$CACHE_URL/ping"
curl --fail-with-body -H "Authorization: Bearer $TURBO_TOKEN" "$CACHE_URL/v8/artifacts/status"

Expect pong, then {"status":"enabled"}.

Result What to check
DNS, TLS, or connection error Confirm the deployment URL and that your network can reach it. Use the HTTPS origin, without a trailing slash or /v8.
HTML or a login redirect A proxy or deployment protection is intercepting the API. Let turbo reach /v8/* with its bearer token.
401 The client token is missing or differs from the server’s TURBO_TOKEN.
500 Read the server logs. See The server returns 500.
Status succeeds but turbo disables remote caching Set the team as well as the URL and token. Check for stale environment variables overriding turbo.json.

/ping only proves the server is up. The remote-hit check tests the complete path.

Builds keep missing

  1. Check whether the tasks were ever uploaded. turbo uploads a task only when it runs it, and a task it restores from the local cache never reaches the server. If you built before connecting the cache, the producing machine keeps getting local hits and the server stays empty. Delete .turbo/cache on that machine and build once. The remote-hit check does exactly this.
  2. Find out where each hit came from. npm exec -- turbo run build --summarize writes a JSON summary to .turbo/runs/, and each task’s cache.source is LOCAL, REMOTE, or absent for a miss.
  3. Confirm that the task has caching enabled. Compare the task hashes in two machines’ run summaries. If the hash changes, turbo sees different inputs. Check Turborepo’s cache troubleshooting guide.
  4. Confirm that clients use the same server and team. A different team uses a different artifact namespace.
  5. Check the first run for upload errors. A task can succeed and still fail to upload its artifact. See Uploads fail.
  6. If signatures are enabled, confirm that every client has the same TURBO_REMOTE_CACHE_SIGNATURE_KEY. A rejected signature is a cache miss. See Sign artifacts.

After changing storage or after an artifact expires, a miss is expected. The task must run and upload again. With KV, newly written artifacts can take 60 seconds or more to reach another region.

If an S3-compatible store reports every artifact as missing, check the endpoint, bucket, permissions, and whether the provider requires S3_FORCE_PATH_STYLE=true. See S3-compatible storage.

Uploads fail

Check the response status and the server’s logs:

  • 413 means the host or a proxy rejected the request size. Compare your artifact size with the platform limits.
  • KV rejects artifacts larger than 25 MiB. Use R2 or an S3-compatible bucket for larger outputs.
  • S3 uploads are buffered in memory. A large upload can exhaust the host’s memory even below its HTTP request limit.
  • A custom adapter must support user metadata. An adapter that rejects metadata cannot store these artifacts.

The storage credentials need read, write, list, and delete permissions. The last two are needed for expiry and storage measurements.

The server returns 500

Look for Invalid server configuration: in the logs. The log line names the missing token, the invalid retention value, or the missing storage. The HTTP response leaves those details out, so the logs are the only place to find them.

Check the server configuration reference and storage selection order. An existing KV or R2 binding takes precedence over S3 variables. Setting S3 variables alone does not switch a Worker away from its bound store.

If configuration is valid, inspect the error’s stack trace and the storage provider’s response. Include those details, with credentials removed, when reporting a bug.

Dashboard stats are unavailable

Request the stats endpoint:

curl --fail-with-body "$CACHE_URL/stats"

"enabled": false means the server couldn’t use its stats database. Look for [stats] errors in the logs and configure a database. The cache keeps working without stats.

On Node.js and Deno, keep .data/db.sqlite on persistent storage. Serverless hosts need a hosted database. After changing a database connection in nitro.config.ts, rebuild the server.

Storage figures are empty or stale

Storage measurements come from the expiry task, not from each upload. A new deployment has no measurements until that task runs successfully.

Run expiry on demand, then refresh the dashboard. This also deletes expired objects. On Netlify and Lambda, schedule the request, because those builds do not run the schedule automatically.

Expiry returns 400

The expiry endpoint requires a JSON body, even when you want the default retention period. Send {} with Content-Type: application/json, as shown in Run expiry on demand.

Report a problem

Include the server version or deployed commit, turbo version, hosting platform, storage backend, and the failing command or request. Include the response status and relevant logs, with tokens and credentials removed. Report bugs in GitHub Issues. Report vulnerabilities through the security policy.

Was this page helpful?