{"record":{"id":"a0a04257de39cd26","repo":"jdx/mise","slug":"remote-task-action-manifest-changed-too-frequently","errorCode":null,"errorMessage":"remote task action manifest changed too frequently","messagePattern":"remote task action manifest changed too frequently","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"warning","filePath":"crates/mise-cache-core/src/agent.rs","lineNumber":671,"sourceCode":"                let _permit = self.remote_transfers.acquire().await?;\n                remote\n                    .put_action_manifest(&selector, &bytes, expected_etag.as_deref())\n                    .await?\n            };\n            match outcome {\n                ManifestPutOutcome::Stored => return Ok(manifest),\n                ManifestPutOutcome::PreconditionFailed => {\n                    let Some((remote_manifest, etag)) = self.get_remote_task_manifest(task).await?\n                    else {\n                        expected_etag = None;\n                        continue;\n                    };\n                    manifest = merge_task_manifests(task, Some(remote_manifest), manifest)?;\n                    expected_etag = Some(etag);\n                }\n            }\n        }\n        bail!(\"remote task action manifest changed too frequently\")\n    }\n\n    /// Return a snapshot of this session's cache activity.\n    pub fn stats(&self) -> AgentStats {\n        AgentStats {\n            session_duration_ns: 0,\n            lookups: self.stats.lookups.load(Ordering::Relaxed),\n            hits: self.stats.hits.load(Ordering::Relaxed),\n            stores: self.stats.stores.load(Ordering::Relaxed),\n            stored_bytes: self.stats.stored_bytes.load(Ordering::Relaxed),\n            verifications: self.stats.verifications.load(Ordering::Relaxed),\n            divergences: self.stats.divergences.load(Ordering::Relaxed),\n            downloaded_bytes: self.stats.downloaded_bytes.load(Ordering::Relaxed),\n            uploaded_bytes: self.stats.uploaded_bytes.load(Ordering::Relaxed),\n            prefetched_actions: self.stats.prefetched_actions.load(Ordering::Relaxed),\n            remote_manifest_lookups: self.stats.remote_manifest_lookups.load(Ordering::Relaxed),\n            remote_manifest_lookup_duration_ns: self\n                .stats","sourceCodeStart":653,"sourceCodeEnd":689,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/crates/mise-cache-core/src/agent.rs#L653-L689","documentation":"put_remote_task_manifest uploads the merged manifest with an If-Match ETag precondition. On PreconditionFailed it re-fetches the remote manifest, merges, and retries — but the loop runs only 4 iterations (for _ in 0..4). If another writer updates the remote manifest on every attempt, the CAS loop gives up and bails.","triggerScenarios":"Four or more concurrent sessions committing manifests for the same 64-hex task identity, each invalidating the others' ETags between get and put; a writer stuck in a rapid update loop on the same manifest key.","commonSituations":"A CI fleet fanning out many jobs for the same task finishing simultaneously against one shared remote cache; a misbehaving client rewriting the manifest entry in a tight loop.","solutions":["Retry the whole commit after a short backoff — begin_task/commit again; contention is usually transient","Stagger concurrent task completions for the same task identity (queue or jitter the writers)","If it persists, identify the client continuously rewriting that manifest entry (buggy writer) and stop it"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"let mut backoff = Duration::from_millis(500);\nloop {\n    match agent.commit_task(&run).await {\n        Ok(_) => break,\n        Err(e) if e.to_string().contains(\"changed too frequently\") && attempt < 3 => {\n            tokio::time::sleep(backoff).await;\n            backoff *= 2;\n            run = agent.begin_task(&task).await?; // fresh baseline + etag\n        }\n        Err(e) => return Err(e),\n    }\n}","preventionTips":["Stagger concurrent commiters for the same task identity (jitter or queue writers)","Treat this error as transient contention, not corruption — do not delete the remote entry for it","Monitor commit retry rates to detect a pathological writer"],"tags":["mise-cache","optimistic-concurrency","etag","retry"],"backgroundTag":"optimistic-concurrency-conflict","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}