{"record":{"id":"539331ac7f56c182","repo":"GraphiteEditor/Graphite","slug":"active-document-is-missing-from-document-ids","errorCode":null,"errorMessage":"Active document is missing from document ids","messagePattern":"Active document is missing from document ids","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"editor/src/messages/portfolio/portfolio_message_handler.rs","lineNumber":1929,"sourceCode":"\t\t\t\tcompare_storage_against_runtime(&gdd, &legacy_network, byte_store.as_ref(), document_id).await;\n\t\t\t}\n\n\t\t\tMessage::Portfolio(PortfolioMessage::DocumentStorageMounted {\n\t\t\t\tdocument_id,\n\t\t\t\treopened,\n\t\t\t\tgdd: Some(gdd),\n\t\t\t})\n\t\t};\n\t\tfuture.into()\n\t}\n\n\t/// Returns an iterator over the open documents in order.\n\tpub fn ordered_document_iterator(&self) -> impl Iterator<Item = &DocumentMessageHandler> {\n\t\tself.document_ids.iter().filter_map(|id| self.document(*id))\n\t}\n\n\tfn document_index(&self, document_id: DocumentId) -> usize {\n\t\tself.document_ids.iter().position(|id| id == &document_id).expect(\"Active document is missing from document ids\")\n\t}\n\n\tpub fn poll_node_graph_evaluation(&mut self, responses: &mut VecDeque<Message>) -> Result<(), String> {\n\t\tlet Some(document_id) = self.active_document_id else {\n\t\t\treturn Err(\"No active document\".to_string());\n\t\t};\n\t\tlet Some(active_document) = self.documents.get_mut(&document_id) else {\n\t\t\treturn Err(\"No active document\".to_string());\n\t\t};\n\n\t\tlet result = self.executor.poll_node_graph_evaluation(active_document, document_id, responses);\n\t\tif result.is_err() {\n\t\t\tlet error = r#\"\n\t\t\t\t<rect x=\"50%\" y=\"50%\" width=\"460\" height=\"100\" transform=\"translate(-230 -50)\" rx=\"4\" fill=\"var(--color-warning-yellow)\" />\n\t\t\t\t<text x=\"50%\" y=\"50%\" dominant-baseline=\"middle\" text-anchor=\"middle\" font-size=\"18\" fill=\"var(--color-2-mildblack)\">\n\t\t\t\t\t<tspan x=\"50%\" dy=\"-24\" font-weight=\"bold\">The document cannot render in its current state.</tspan>\n\t\t\t\t\t<tspan x=\"50%\" dy=\"24\">Undo to go back, if available, or check for error details</tspan>\n\t\t\t\t\t<tspan x=\"50%\" dy=\"24\">by clicking the <tspan font-style=\"italic\">Node Graph</tspan> button up at the top right.</tspan>","sourceCodeStart":1911,"sourceCodeEnd":1947,"githubUrl":"https://github.com/GraphiteEditor/Graphite/blob/c507b356453361e31638b8bff8f6d46b6da2961e/editor/src/messages/portfolio/portfolio_message_handler.rs#L1911-L1947","documentation":"document_index does self.document_ids.iter().position(|id| id == &document_id).expect(\"Active document is missing from document ids\"). PortfolioMessageHandler keeps documents in a HashMap plus an order Vec (document_ids); this expect asserts the two stay in sync. It panics when a caller asks for the index of a document (usually the active one) whose id is absent from the ordering Vec - a state-desync bug where a document was removed from one structure but not the other.","triggerScenarios":"A close/remove path deletes from self.documents but forgets self.document_ids (or vice versa), then any operation needing document ordering (tab order, activation after close) calls document_index on the orphaned id.","commonSituations":"New document-close or session-restore code paths updating only one of the two collections; undo of document open; bugs in reordering logic dropping ids.","solutions":["Change document_index to return Option<usize> (or use position().unwrap_or default with a logged error) so desync degrades instead of panicking","Audit every mutation of self.documents to also mutate self.document_ids in the same message handler","Add a debug assertion after close/open messages that the map keys and the Vec contents match exactly"],"exampleFix":"// before\nfn document_index(&self, document_id: DocumentId) -> usize {\n\tself.document_ids.iter().position(|id| id == &document_id).expect(\"Active document is missing from document ids\")\n}\n\n// after\nfn document_index(&self, document_id: DocumentId) -> Option<usize> {\n\tself.document_ids.iter().position(|id| id == &document_id)\n}","handlingStrategy":"validation","validationCode":"if let Some(index) = self.document_ids.iter().position(|id| id == &document_id) {\n\t// use index\n} else {\n\tlog::error!(\"document {document_id:?} missing from document_ids; desync\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Update self.documents and self.document_ids only through one helper so they cannot diverge","Return Option<usize> from index lookups instead of expecting","Add a debug assertion that the map keys equal the Vec ids after open/close/reorder messages"],"tags":["rust","state-desync","portfolio","collections","panic"],"backgroundTag":"state-desync-missing-id","analyzedSha":"c507b356453361e31638b8bff8f6d46b6da2961e","analyzedAt":"2026-08-16T21:57:18.596Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}