{"record":{"id":"4b44d008e2546290","repo":"rust-lang/rust-analyzer","slug":"invariant-violation-file-emitted-multiple-times","errorCode":null,"errorMessage":"Invariant violation: file emitted multiple times.","messagePattern":"Invariant violation: file emitted multiple times\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/rust-analyzer/src/cli/scip.rs","lineNumber":229,"sourceCode":"            }\n\n            if occurrences.is_empty() {\n                continue;\n            }\n\n            let position_encoding =\n                scip_types::PositionEncoding::UTF8CodeUnitOffsetFromLineStart.into();\n            documents.push(scip_types::Document {\n                relative_path,\n                language: \"rust\".to_owned(),\n                occurrences,\n                symbols,\n                text: String::new(),\n                position_encoding,\n                special_fields: Default::default(),\n            });\n            if !file_ids_emitted.insert(file_id) {\n                panic!(\"Invariant violation: file emitted multiple times.\");\n            }\n        }\n\n        // Collect all symbols referenced by the files but not defined within them.\n        let mut external_symbols = Vec::new();\n        for id in token_ids_referenced.difference(&token_ids_emitted) {\n            let id = *id;\n            let token = si.tokens.get(id).unwrap();\n\n            let Some(definition) = token.definition else {\n                continue;\n            };\n\n            let file_id = definition.file_id;\n            let Some(relative_path) = get_relative_filepath(&vfs, &root, file_id) else { continue };\n            let line_index = get_line_index(db, file_id);\n            let text_range = definition.range;\n            if file_ids_emitted.contains(&file_id) {","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/crates/rust-analyzer/src/cli/scip.rs#L211-L247","documentation":"The `scip` CLI command emits one SCIP document per source file. It tracks emitted files in a `file_ids_emitted` set and asserts each `file_id` is inserted exactly once; a duplicate means the index produced two documents for the same file, violating SCIP's one-document-per-file invariant, so `run` panics.","triggerScenarios":"Running `rust-analyzer scip` over a workspace where the same file (e.g. via overlapping crate roots, include-like duplication, or a token-mapping bug) yields more than one emission during the document loop.","commonSituations":"Workspaces with duplicated/aliased crate data; a rust-analyzer bug in file-id to document mapping; generated or macro-expanded files mapped back to the same file id twice.","solutions":["Update rust-analyzer / the scip CLI to the latest version, as duplicate emission usually indicates an index bug","Report the workspace layout that reproduces the duplicate (minimize to the offending crate configuration)","As a workaround, deduplicate documents by file id before serialization in a local patch"],"exampleFix":"// before\nif !file_ids_emitted.insert(file_id) {\n    panic!(\"Invariant violation: file emitted multiple times.\");\n}\n// after (workaround)\nif !file_ids_emitted.insert(file_id) {\n    eprintln!(\"skipping duplicate document for {file_id:?}\");\n    continue;\n}","handlingStrategy":"try-catch","validationCode":"// pre-check duplicates when possible\nlet mut seen = std::collections::HashSet::new();\nfor doc in documents {\n    assert!(seen.insert(doc.file_id));\n}","typeGuard":null,"tryCatchPattern":"std::panic::catch_unwind(|| scip::run(args))\n    .err()\n    .map(|_| eprintln!(\"scip generation produced duplicate file documents; report workspace layout\"));","preventionTips":["Update to the latest rust-analyzer where scip duplicate-emission bugs may be fixed","Minimize and report workspaces that trigger duplicate file ids (aliased crate roots, generated files)","Deduplicate documents by file id before serializing SCIP output","Run the scip export on a clean, non-overlapping crate graph when possible"],"tags":["rust","cli","scip","index","invariant-violation","panic"],"backgroundTag":"duplicate-document-emission","analyzedSha":"e8f7e90aa3e7b26aa9a000200f606c1078da99ec","analyzedAt":"2026-09-03T21:08:06.959Z","contentChangedAt":"2026-09-03T21:08:06.959Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}