{"record":{"id":"551849ff1a4a0973","repo":"libnyanpasu/clash-nyanpasu","slug":"staged-resource-is-missing-and-target-hash-does-no","errorCode":null,"errorMessage":"staged resource is missing and target hash does not match","messagePattern":"staged resource is missing and target hash does not match","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/service/profile_file.rs","lineNumber":910,"sourceCode":"            Err(error) => return Err(error).context(\"inspect ready symlink\"),\n        }\n        Ok(ready)\n    }\n\n    fn promote_resource(\n        &self,\n        root: &Path,\n        operation_id: &str,\n        target: &Path,\n        expected_hash: &str,\n    ) -> anyhow::Result<()> {\n        self.ensure_managed_parent(target)?;\n        Self::ensure_replaceable_target(target)?;\n        let Some(resource) = Self::read_staged_resource(root, operation_id, expected_hash)? else {\n            if Self::path_hash(target)? == expected_hash {\n                return Ok(());\n            }\n            bail!(\"staged resource is missing and target hash does not match\");\n        };\n\n        // Static symlink/reparse validation is mandatory. A same-user parent\n        // swap after this recheck is outside this desktop app's local trust\n        // boundary; descriptor-based confinement is intentionally not added.\n        match resource {\n            StoredResource::File { path } => {\n                self.ensure_managed_parent(target)?;\n                Self::ensure_replaceable_target(target)?;\n                replace_atomic(&path, target).with_context(|| {\n                    format!(\n                        \"promote staged file {} -> {}\",\n                        path.display(),\n                        target.display()\n                    )\n                })?;\n            }\n            StoredResource::Symlink {","sourceCodeStart":892,"sourceCodeEnd":928,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/service/profile_file.rs#L892-L928","documentation":"promote_resource could not read the staged resource for the operation, and the target file at its final location does not hash to the expected value. Together this means neither the staging area nor the target holds the verified content, so the transaction cannot be considered complete and it fails instead of silently promoting unverified data.","triggerScenarios":"promote() called when the staged resource file is missing (deleted, never written, or lost across journal locations) AND the target's content hash differs from expected_hash — e.g. an interrupted prepare_materialization plus a partially modified target.","commonSituations":"Disk cleanup removed staging files between runs; a crash occurred after journal creation but before staging completed; the target was edited or corrupted by another process; migrating staging dirs between machines.","solutions":["Re-download/re-materialize the profile resource: create a new operation via prepare_materialization so the staged file is recreated, then promote.","Restore the staged resource file for this operation_id (correct hash) and rerun promote.","If the target is actually correct, recompute its hash and confirm expected_hash in the journal matches; a stale journal hash is the real bug.","Run compensate to roll the operation back cleanly before retrying."],"exampleFix":"// before\n// staged file deleted by cleanup, target no longer matches journal hash\n// after\nlet op_id = client.prepare_materialization(&managed_path, resource).await?;\nclient.promote(root, &op_id).await?;","handlingStrategy":"retry","validationCode":"// caller-side sanity check before promote\nlet staged_exists = staged_resource_path(root, op_id).exists();\nlet target_hash = hash_file(&target_path)?;\nif !staged_exists && target_hash != expected_hash {\n    // re-materialize instead of promoting\n    let new_id = client.prepare_materialization(&managed_path, resource).await?;\n}","typeGuard":"fn staged_resource_available(root: &Path, op_id: &str, expected: &str) -> bool {\n    read_staged_resource_hash(root, op_id).map(|h| h == expected).unwrap_or(false)\n}","tryCatchPattern":"match client.promote(root, op_id).await {\n    Err(e) if e.to_string().contains(\"staged resource is missing\") => {\n        let fresh = client.prepare_materialization(&managed_path, resource).await?;\n        client.promote(root, &fresh).await\n    }\n    r => r,\n}","preventionTips":["Promote promptly after prepare; don't leave operations staged across cleanup windows.","Exclude staging dirs from disk cleaners and tmp reapers.","Verify profile files aren't edited concurrently by editors or sync clients.","Reconcile journals at startup to detect lost staged resources early."],"tags":["filesystem","integrity","hash"],"backgroundTag":"checksum-mismatch","analyzedSha":"f7dbce2997c633e484f54788035e770b3ee99773","analyzedAt":"2026-09-08T01:24:59.197Z","contentChangedAt":"2026-09-08T01:24:59.197Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}