{"record":{"id":"bddf3e01114a604a","repo":"GraphiteEditor/Graphite","slug":"just-checked-there-s-one-entry","errorCode":null,"errorMessage":"just checked there's one entry","messagePattern":"just checked there's one entry","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"editor/src/messages/portfolio/portfolio_message_handler.rs","lineNumber":500,"sourceCode":"\t\t\t\t\t\tlet base = format!(\"{stem}.{FILE_EXTENSION}\");\n\t\t\t\t\t\tlet unique = match used_names.get(&base).copied() {\n\t\t\t\t\t\t\tNone => {\n\t\t\t\t\t\t\t\tused_names.insert(base.clone(), 1);\n\t\t\t\t\t\t\t\tbase\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tSome(n) => {\n\t\t\t\t\t\t\t\tused_names.insert(base.clone(), n + 1);\n\t\t\t\t\t\t\t\tformat!(\"{stem} ({n}).{FILE_EXTENSION}\")\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t\t(unique, content.as_bytes().to_vec())\n\t\t\t\t\t})\n\t\t\t\t\t.collect();\n\n\t\t\t\tconst FOLDER_NAME: &str = \"Graphite Recovered Documents\";\n\n\t\t\t\tif files.len() == 1 {\n\t\t\t\t\tlet (filename, content) = files.into_iter().next().expect(\"just checked there's one entry\");\n\t\t\t\t\tresponses.add(FrontendMessage::TriggerSaveFile {\n\t\t\t\t\t\tname: filename,\n\t\t\t\t\t\tfolder: None,\n\t\t\t\t\t\tcontent: serde_bytes::ByteBuf::from(content),\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tmatch build_recovery_zip(&files) {\n\t\t\t\t\t\tOk(zip_bytes) => responses.add(FrontendMessage::TriggerSaveFile {\n\t\t\t\t\t\t\tname: format!(\"{FOLDER_NAME}.zip\"),\n\t\t\t\t\t\t\tfolder: None,\n\t\t\t\t\t\t\tcontent: serde_bytes::ByteBuf::from(zip_bytes),\n\t\t\t\t\t\t}),\n\t\t\t\t\t\tErr(e) => {\n\t\t\t\t\t\t\tlog::error!(\"Failed to build recovery zip: {e}\");\n\t\t\t\t\t\t\tresponses.add(DialogMessage::DisplayDialogError {\n\t\t\t\t\t\t\t\ttitle: \"Failed to download\".to_string(),\n\t\t\t\t\t\t\t\tdescription: format!(\"Could not bundle the failed documents for download.\\n\\n{e}\"),\n\t\t\t\t\t\t\t});","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/portfolio/portfolio_message_handler.rs#L482-L518","documentation":"In the crash-recovery flow, files is a collected Vec and the code branches on files.len() == 1, then immediately does files.into_iter().next().expect(\"just checked there's one entry\"). This expect is an invariant annotation for the compiler: the length was just checked, so next() must yield Some. It is unreachable unless the two collections diverge (e.g., a future refactor checks one Vec but iterates another).","triggerScenarios":"Practically unreachable as written; would fire only if the len()==1 check and the iterated collection become different collections after refactoring, or concurrency were introduced between check and use (there is none here).","commonSituations":"Refactors that split or rebuild the files Vec between the length check and iteration; copy-paste of this pattern into async code where the invariant no longer holds.","solutions":["Prefer if let Some((filename, content)) = files.into_iter().next() which makes the branch and the extraction one atomic pattern","Keep the length check and the drain adjacent so refactors cannot separate them","If kept as-is, treat any hit of this expect as a logic-bug signal, not an environment problem"],"exampleFix":"// before\nif files.len() == 1 {\n\tlet (filename, content) = files.into_iter().next().expect(\"just checked there's one entry\");\n\t// ...\n}\n\n// after\nif let Some((filename, content)) = files.into_iter().next_if(|_| files.len() == 1) {\n\t// ...\n}","handlingStrategy":"validation","validationCode":"if files.len() == 1 {\n\tdebug_assert_eq!(files.len(), 1);\n\tif let Some((filename, content)) = files.into_iter().next() { /* handle single file */ }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Fuse the length check and the extraction into one pattern (if let) so refactors cannot split the invariant","Treat any hit of such an 'unreachable' expect as a logic bug introduced by recent changes","Avoid duplicating this pattern in async contexts where state can change between check and use"],"tags":["rust","invariant","unreachable","recovery"],"backgroundTag":"invariant-assertion-panic","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}