vercel/turborepo · warning

{err}

Error message

{err}

What it means

The AsyncCacheWrapper (crates/turborepo-cache/src/async_cache.rs) fires background tokio tasks that write cache entries via real_cache.put(). A failed put is non-fatal — the build already succeeded — so the error is only surfaced as a warning, and only while the warning count stays under WARNING_CUTOFF; after that, failures are silent.

Source

Thrown at crates/turborepo-cache/src/async_cache.rs:95

                        let Ok(permit) = semaphore.clone().acquire_owned().await else {
                            break;
                        };
                        let real_cache = real_cache.clone();
                        let warnings = warnings.clone();
                        let worker_span = tracing::span!(Level::TRACE, "cache worker: cache PUT");
                        workers.push(tokio::spawn(
                            async move {
                                if let Err(err) =
                                    real_cache.put(&anchor, &key, &files, duration).await
                                {
                                    let num_warnings =
                                        warnings.load(std::sync::atomic::Ordering::Acquire);
                                    if num_warnings <= WARNING_CUTOFF {
                                        warnings.store(
                                            num_warnings + 1,
                                            std::sync::atomic::Ordering::Release,
                                        );
                                        turborepo_log::warn(
                                            turborepo_log::Source::turbo(
                                                turborepo_log::Subsystem::Cache,
                                            ),
                                            format!("{err}"),
                                        )
                                        .emit();
                                    }
                                }
                                // Release permit once we're done with the write
                                drop(permit);
                            }
                            .instrument(worker_span),
                        ))
                    }
                    WorkerRequest::Flush(callback) => {
                        // Wait on all workers to finish writing
                        while let Some(worker) = workers.next().await {
                            let _ = worker;

View on GitHub (pinned to f9245100cf)

Solutions

  1. Read the embedded {err} — it is the underlying cache error (io error for local, CacheError for remote) and names the real problem.
  2. For local cache errors: free disk space and verify write permission on TURBO_CACHE_DIR.
  3. For remote cache errors: check token/connectivity (turbo login / TURBO_TOKEN, TURBO_TEAM, api reachability).
  4. Remember warnings are capped: verify cache health with cache hit/miss stats rather than assuming no warnings means every put succeeded.

Example fix

# before: cache dir on nearly-full volume
export TURBO_CACHE_DIR=/mnt/small-disk/turbo  # -> cache put io errors (ENOSPC)

# after: point at space with headroom, or rely on remote cache
export TURBO_CACHE_DIR=/mnt/big-disk/turbo
Defensive patterns

Strategy: fallback

Validate before calling

#!/usr/bin/env bash
# local cache writable + space check before the run
CACHE_DIR="${TURBO_CACHE_DIR:-.turbo/cache}"
mkdir -p "$CACHE_DIR" && touch "$CACHE_DIR/.w" && rm "$CACHE_DIR/.w" \
  || echo "warn: local cache writes may fail" >&2
command -v curl >/dev/null && curl -sf -o /dev/null "$TURBO_API" \
  || echo "warn: remote cache api unreachable — puts will warn" >&2
turbo run build

Try / catch

// AsyncCacheWrapper put errors surface as warnings, not task failures.
// If you wrap AsyncCacheWrapper yourself, mirror that contract:
if let Err(err) = async_cache.put(&anchor, &key, &files, duration).await {
    tracing::warn!(?err, "cache put failed; continuing without caching");
    // do NOT propagate: the build result is already valid
}

Prevention

When it happens

Trigger: Any put() error inside the spawned worker: disk full or permission errors writing the local cache, file locking conflicts, or remote cache upload failures when the async wrapper fronts a multiplexed cache. Warnings are rate-limited by an atomic counter shared across workers.

Common situations: Disk pressure in CI (local cache writes failing near the end of a run), read-only or quota-limited cache dirs, remote cache outages during long builds — users see a handful of warnings then nothing, while cache hit rates quietly drop.

Related errors


AI-assisted analysis of vercel/turborepo@f9245100cf (2026-08-17). Data as JSON: /api/errors/8354df5866c3f8b5. Report an issue: GitHub.