{"record":{"id":"0380be7c4208ad16","repo":"libnyanpasu/clash-nyanpasu","slug":"materialization-has-multiple-staged-resources","errorCode":null,"errorMessage":"materialization has multiple staged resources","messagePattern":"materialization has multiple staged resources","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/service/profile_file.rs","lineNumber":776,"sourceCode":"            Err(error) => {\n                return Err(error)\n                    .with_context(|| format!(\"inspect staged file {}\", file_path.display()));\n            }\n        };\n        let link_metadata = match std::fs::symlink_metadata(&link_path) {\n            Ok(metadata) => Some(metadata),\n            Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,\n            Err(error) => {\n                return Err(error).with_context(|| {\n                    format!(\n                        \"inspect staged symlink specification {}\",\n                        link_path.display()\n                    )\n                });\n            }\n        };\n        if file_metadata.is_some() && link_metadata.is_some() {\n            bail!(\"materialization has multiple staged resources\");\n        }\n        if let Some(metadata) = file_metadata {\n            if is_symlink_or_reparse(&metadata) || !metadata.is_file() {\n                bail!(\"staged file is not a regular file\");\n            }\n            let content = std::fs::read(&file_path)?;\n            if hash_tagged(b\"file\", &content) != expected_hash {\n                bail!(\"staged file hash mismatch\");\n            }\n            return Ok(Some(StoredResource::File { path: file_path }));\n        }\n        if let Some(metadata) = link_metadata {\n            if is_symlink_or_reparse(&metadata) || !metadata.is_file() {\n                bail!(\"staged symlink specification is not a regular file\");\n            }\n            let target = std::fs::read_to_string(&link_path)?;\n            if hash_tagged(b\"symlink\", target.as_bytes()) != expected_hash {\n                bail!(\"staged symlink hash mismatch\");","sourceCodeStart":758,"sourceCodeEnd":794,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/service/profile_file.rs#L758-L794","documentation":"During an external profile materialization, read_staged_resource expects at most one staged resource (a file blob OR a symlink-spec file) per operation_id under the staging root. It found both the staged file path and the staged symlink-spec path present simultaneously. This means the staging directory is in an inconsistent state, so the code refuses to guess which resource is the real one and aborts.","triggerScenarios":"Both stage_file_path(root, operation_id) and stage_file_path-like stage_link_path(root, operation_id) exist on disk when read_staged_resource is called for the same operation_id — e.g. a previous materialization attempt for the same operation_id staged a file and then re-ran stage_resource with a Symlink resource without cleanup, or leftover staging directories from a crashed run were reused under the same operation_id.","commonSituations":"A materialization operation crashed partway and was retried with the same operation ID but a different resource kind; manual copying/restoring of the staging directory; concurrent materialization runs sharing one operation_id.","solutions":["Delete the stale staging directory for this operation_id (both staged file and staged link files) and re-run the materialization so only one resource is staged.","Re-run the materialization with a fresh operation_id so a new, empty staging area is used.","Check for concurrent processes/tasks using the same operation_id and serialize them.","If reproducible, inspect stage_resource / cleanup paths in profile_file.rs to confirm the old staged resource is removed before staging a different kind."],"exampleFix":"// before: reusing operation_id with mixed staged leftovers\nstage_resource(root, \"op-1\", &MaterializationResource::File { content });\nstage_resource(root, \"op-1\", &MaterializationResource::Symlink { target }); // leftovers -> two staged resources\n// after: clean staging before (re)staging, or use a fresh id\nstd::fs::remove_dir_all(stage_root.join(\"op-1\")).ok();\nstage_resource(root, \"op-1\", &MaterializationResource::Symlink { target });","handlingStrategy":"try-catch","validationCode":"let file_path = stage_file_path(root, op_id);\nlet link_path = stage_link_path(root, op_id);\nif file_path.exists() && link_path.exists() {\n    // clean stale staging before invoking the operation\n    std::fs::remove_dir_all(stage_dir(root, op_id))?;\n}","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"multiple staged resources\") => {\n        std::fs::remove_dir_all(stage_dir(root, op_id))?;\n        retry_with_fresh_operation_id()\n    }\n    other => other,\n}","preventionTips":["Use a unique operation_id per materialization attempt (uuid), never reuse across retries with different resource kinds.","Always wipe the staging directory for an operation_id before staging.","Serialize materializations that could share an operation_id."],"tags":["filesystem","state-inconsistency","profile-materialization","rust"],"backgroundTag":"internal-invariant-violation","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"}