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
- Read the embedded {err} — it is the underlying cache error (io error for local, CacheError for remote) and names the real problem.
- For local cache errors: free disk space and verify write permission on TURBO_CACHE_DIR.
- For remote cache errors: check token/connectivity (turbo login / TURBO_TOKEN, TURBO_TEAM, api reachability).
- 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
- Treat cache put warnings as a health signal: alert when a pipeline emits any, since failures are capped and can go silent.
- Keep disk headroom and valid credentials so background puts succeed.
- Check cache hit/miss counters after runs rather than assuming no news is good news.
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.