{"record":{"id":"30997032ed007bd0","repo":"libnyanpasu/clash-nyanpasu","slug":"ready-symlink-target-mismatch","errorCode":null,"errorMessage":"ready symlink target mismatch","messagePattern":"ready symlink target mismatch","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/service/profile_file.rs","lineNumber":883,"sourceCode":"            }\n            let target = std::fs::read_to_string(&link_path)?;\n            return Ok(Some(StoredResource::Symlink {\n                target: ExternalProfilePath::new(target)?,\n            }));\n        }\n        Ok(None)\n    }\n\n    fn create_ready_link(\n        root: &Path,\n        operation_id: &str,\n        target: &ExternalProfilePath,\n    ) -> anyhow::Result<PathBuf> {\n        let ready = Self::ready_link_path(root, operation_id);\n        match std::fs::symlink_metadata(&ready) {\n            Ok(metadata) if metadata.file_type().is_symlink() => {\n                if std::fs::read_link(&ready)? != target.as_path() {\n                    bail!(\"ready symlink target mismatch\");\n                }\n            }\n            Ok(_) => bail!(\"ready symlink path is occupied by an unexpected node\"),\n            Err(error) if error.kind() == std::io::ErrorKind::NotFound => {\n                create_file_symlink(target.as_path(), &ready)\n                    .with_context(|| format!(\"create staged symlink {}\", ready.display()))?;\n                sync_directory(ready.parent().expect(\"ready symlink has parent\"))?;\n            }\n            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,","sourceCodeStart":865,"sourceCodeEnd":901,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/service/profile_file.rs#L865-L901","documentation":"During create_ready_link, the staged 'ready' symlink already exists and points to a different path than the ExternalProfilePath supplied for this materialization operation. The function is idempotent: an existing ready symlink with the matching target is accepted, but any divergence means stale or corrupted staging state from another operation, so it aborts rather than promoting the wrong file.","triggerScenarios":"create_ready_link called with a target path while a ready symlink for the same operation_id already exists pointing elsewhere — e.g. a retried operation whose target was regenerated with a different temp path, or an operation_id colliding with a previous run.","commonSituations":"Re-running a profile download/materialization after a partial failure; reusing an operation id across attempts; manual edits or cleanup tools replacing the ready symlink.","solutions":["Delete the stale ready symlink for this operation_id and rerun the materialization so it is recreated with the current target.","Run the journal compensate/reconcile flow for this operation_id to clean up inconsistent staged artifacts.","Generate a fresh operation_id via allocate_operation_id and start the materialization over.","Inspect the ready symlink target manually (ls -l / fs::read_link) to confirm it belongs to a different operation before removing it."],"exampleFix":"// before\n// stale ready symlink from a failed run points at old temp file\n// after\n// compensate(operation_id) to clear staged artifacts, then re-run promote\nlet _ = client.compensate(root, operation_id).await?;\nclient.promote(root, operation_id).await?;","handlingStrategy":"retry","validationCode":"if let Ok(meta) = std::fs::symlink_metadata(&ready_path) {\n    if meta.file_type().is_symlink() && std::fs::read_link(&ready_path)? != expected_target {\n        std::fs::remove_file(&ready_path)?; // clear stale link before retry\n    }\n}","typeGuard":"fn ready_link_matches(ready: &Path, target: &Path) -> bool {\n    std::fs::symlink_metadata(ready)\n        .map(|m| m.file_type().is_symlink() && std::fs::read_link(ready).map(|p| p == target).unwrap_or(false))\n        .unwrap_or(false)\n}","tryCatchPattern":"match create_ready_link(root, &target) {\n    Err(e) if e.to_string().contains(\"ready symlink target mismatch\") => {\n        let _ = std::fs::remove_file(Self::ready_link_path(root, &op_id));\n        create_ready_link(root, &target) // retry once after cleanup\n    }\n    r => r,\n}","preventionTips":["Always run compensate/reconcile for an operation id before reusing it with new content.","Never reuse operation ids across materialization attempts; allocate a fresh id each time.","Don't let external tools touch the staging directory.","Log the previous symlink target when clearing mismatches to diagnose re-targeting."],"tags":["filesystem","symlink","staging"],"backgroundTag":"file-already-exists","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"}