Back to monorepo orchestration Target affected workspaces Configure turbo pipelines Compare the Nx approach

Debugging Why a Turborepo Task Is Never Cached

A task always executes and never replays from cache, even on identical inputs. This page shows how to find why Turborepo refuses to cache a task and how to make it cacheable.

Exact symptoms and error messages

Exact symptoms and error messages Exact symptoms and error messages in production JavaScript package workflows. Exact symptoms and error messages Exact symptoms and error messages in production JavaScript package workflows.
Exact symptoms and error messages — the core idea of this section at a glance.
web:build: cache miss, executing 1a2b3c...
# run again, no changes
web:build: cache miss, executing 1a2b3c...

Root cause analysis

Turborepo will not cache a task that declares no outputs, is marked cache: false, is persistent, or writes outside its declared output globs. Without knowing what a task produces, Turborepo has nothing to store or restore, so it re-executes every time. Correct output declaration is core to Remote Caching Setup and the deterministic-output rules in Turborepo Pipeline Configuration.

Root cause analysis Turborepo will not cache a task that declares no outputs, is marked cache: false, is persistent, or writes outside its d Root cause analysis Turborepo will not cache a task that declares no outputs, is marked cache: false, is persistent, or writes outside its declared output globs.
Root cause analysis — the core idea of this section at a glance.

Turborepo will not cache a task it cannot describe the outputs of. If a task declares no outputs, is explicitly cache: false, is marked persistent, or writes files outside its declared output globs, there is nothing for Turborepo to store or restore, so it re-executes every time. The fix is almost always to declare what the task produces accurately, so the cache has something concrete to save and replay.

A subtler cause is outputs written outside the declared globs. If build declares dist/** but also emits a manifest to .cache/, that file is invisible to the cache — restoring from cache produces an incomplete result, so in practice you disable caching to avoid the corruption, and the task never replays. Declaring every output path is what makes a cached restore complete and therefore trustworthy.

Resolution and configuration patch

Declare the task's outputs (and remove any accidental cache: false):

Resolution and configuration patch Declare the task's outputs (and remove any accidental cache: false): Resolution and configuration patch Declare the task's outputs (and remove any accidental cache: false):
Resolution and configuration patch — the core idea of this section at a glance.
// turbo.json
{
  "tasks": {
    "build": {
      "outputs": ["dist/**", ".next/**", "!.next/cache/**"],
      "cache": true
    }
  }
}

Ensure the build writes only into declared paths; artifacts written elsewhere are invisible to the cache and defeat replay.

CLI validation and debug commands

CLI validation and debug commands CLI validation and debug commands in production JavaScript package workflows. CLI validation and debug commands CLI validation and debug commands in production JavaScript package workflows.
CLI validation and debug commands — the core idea of this section at a glance.
# See why a task is not cached
pnpm turbo run build --summarize
# Confirm outputs are declared and produced
pnpm turbo run build --dry=json | jq '.tasks[0].outputs'

Prevention and CI guardrails

  • Declare outputs for every cacheable task; an empty list disables caching.
  • Reserve cache: false for genuinely non-deterministic tasks only.
  • Keep persistent tasks (dev servers) out of the cached build graph.
  • Verify with --summarize that produced files fall inside declared output globs.
Prevention and CI guardrails Prevention and CI guardrails in production JavaScript package workflows. Prevention and CI guardrails Prevention and CI guardrails in production JavaScript package workflows.
Prevention and CI guardrails — the core idea of this section at a glance.

Declaring outputs completely

The single most common reason a task never caches is an incomplete outputs list. Turborepo caches exactly the files you declare, so any artifact the task produces but does not list is lost on a cache restore — which makes caching unsafe and effectively forces you to turn it off.

Complete outputs List every produced path so a restore is byte-complete. list every dir dist, .next negate volatile !.next/cache restore complete safe to cache
A complete outputs list makes a cached restore reproduce the full build.
// turbo.json
{
  "tasks": {
    "build": { "outputs": ["dist/**", ".next/**", "!.next/cache/**"] }
  }
}

List every directory the build writes, and use negations to exclude volatile subpaths (like .next/cache) that should not be part of the cached artifact. The test is simple: delete the outputs, restore from cache, and confirm the result is byte-complete. If anything is missing, the outputs list is too narrow — widen it until a restore reproduces the full build, at which point the task caches correctly and replays instead of re-running.

Persistent and non-deterministic tasks

Some tasks genuinely should not cache, and recognizing them prevents wasted debugging. A dev server is persistent — it never exits, so it has no final output to store. A task that embeds a timestamp or a random value is non-deterministic, so a 'cached' result would be wrong. For these, disabling caching is correct, not a bug.

When not to cache Which tasks legitimately should not cache. Task Cache? Why build yes deterministic outputs dev server no persistent, no output timestamped no non-deterministic
Cache everything deterministic; exclude only persistent or varying tasks.
{
  "tasks": {
    "dev": { "persistent": true, "cache": false },
    "build": { "outputs": ["dist/**"] }
  }
}

Mark long-running tasks persistent so Turborepo keeps them out of the cached graph, and reserve cache: false for tasks whose output legitimately varies run to run. The goal is to cache everything deterministic and exclude only what truly cannot be — rather than blanket-disabling caching because one task misbehaves, which throws away the replay benefit for every well-behaved task alongside it.

Frequently Asked Questions

Why does a task with no outputs still not cache?

Turborepo caches a task's declared outputs; with none declared it has nothing to store, so it cannot replay and re-runs every time. Declare the files the task produces.

Should dev servers be cached?

No — mark long-running tasks persistent: true and keep them out of the cached build graph. Caching only makes sense for tasks that produce deterministic file outputs and then exit.

Why does a task with no outputs never cache?

Turborepo caches a task's declared outputs; with none declared it has nothing to store, so it cannot replay and re-runs every time. Declare every directory the task produces.

How do I know my outputs list is complete?

Delete the outputs, restore from cache, and check the result is byte-complete. Anything missing means the list is too narrow — widen it (with negations for volatile subpaths) until a restore reproduces the full build.

Should a dev server be cached?

No. Mark it persistent: true so Turborepo keeps it out of the cached graph. Caching only applies to tasks that produce deterministic file outputs and then exit.

Related

Remote Caching Setup