{"record":{"id":"8354df5866c3f8b5","repo":"vercel/turborepo","slug":"err","errorCode":null,"errorMessage":"{err}","messagePattern":"\\{err\\}","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"crates/turborepo-cache/src/async_cache.rs","lineNumber":95,"sourceCode":"                        let Ok(permit) = semaphore.clone().acquire_owned().await else {\n                            break;\n                        };\n                        let real_cache = real_cache.clone();\n                        let warnings = warnings.clone();\n                        let worker_span = tracing::span!(Level::TRACE, \"cache worker: cache PUT\");\n                        workers.push(tokio::spawn(\n                            async move {\n                                if let Err(err) =\n                                    real_cache.put(&anchor, &key, &files, duration).await\n                                {\n                                    let num_warnings =\n                                        warnings.load(std::sync::atomic::Ordering::Acquire);\n                                    if num_warnings <= WARNING_CUTOFF {\n                                        warnings.store(\n                                            num_warnings + 1,\n                                            std::sync::atomic::Ordering::Release,\n                                        );\n                                        turborepo_log::warn(\n                                            turborepo_log::Source::turbo(\n                                                turborepo_log::Subsystem::Cache,\n                                            ),\n                                            format!(\"{err}\"),\n                                        )\n                                        .emit();\n                                    }\n                                }\n                                // Release permit once we're done with the write\n                                drop(permit);\n                            }\n                            .instrument(worker_span),\n                        ))\n                    }\n                    WorkerRequest::Flush(callback) => {\n                        // Wait on all workers to finish writing\n                        while let Some(worker) = workers.next().await {\n                            let _ = worker;","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/vercel/turborepo/blob/f9245100cf0d31d96628804ead485f6bf226e55a/crates/turborepo-cache/src/async_cache.rs#L77-L113","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before: cache dir on nearly-full volume\nexport TURBO_CACHE_DIR=/mnt/small-disk/turbo  # -> cache put io errors (ENOSPC)\n\n# after: point at space with headroom, or rely on remote cache\nexport TURBO_CACHE_DIR=/mnt/big-disk/turbo","handlingStrategy":"fallback","validationCode":"#!/usr/bin/env bash\n# local cache writable + space check before the run\nCACHE_DIR=\"${TURBO_CACHE_DIR:-.turbo/cache}\"\nmkdir -p \"$CACHE_DIR\" && touch \"$CACHE_DIR/.w\" && rm \"$CACHE_DIR/.w\" \\\n  || echo \"warn: local cache writes may fail\" >&2\ncommand -v curl >/dev/null && curl -sf -o /dev/null \"$TURBO_API\" \\\n  || echo \"warn: remote cache api unreachable — puts will warn\" >&2\nturbo run build","typeGuard":null,"tryCatchPattern":"// AsyncCacheWrapper put errors surface as warnings, not task failures.\n// If you wrap AsyncCacheWrapper yourself, mirror that contract:\nif let Err(err) = async_cache.put(&anchor, &key, &files, duration).await {\n    tracing::warn!(?err, \"cache put failed; continuing without caching\");\n    // do NOT propagate: the build result is already valid\n}","preventionTips":["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."],"tags":["turborepo-cache","async","cache-write","background-task","io"],"backgroundTag":"cache-write-failed","analyzedSha":"f9245100cf0d31d96628804ead485f6bf226e55a","analyzedAt":"2026-08-17T10:46:15.696Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}