{"record":{"id":"aac60bec07adb6fb","repo":"libnyanpasu/clash-nyanpasu","slug":"runtime-snapshot-node-does-not-exist","errorCode":null,"errorMessage":"runtime snapshot node does not exist","messagePattern":"runtime snapshot node does not exist","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/client/runtime_inspection.rs","lineNumber":94,"sourceCode":"                .collect(),\n        }\n    }\n\n    fn inspection_content(\n        &self,\n        snapshot_id: &str,\n        node_id: u32,\n    ) -> anyhow::Result<RuntimeInspectionContent> {\n        anyhow::ensure!(\n            self.inspection_id == snapshot_id,\n            \"runtime snapshot changed; refresh the inspection\"\n        );\n        let node = self\n            .inspection\n            .graph\n            .nodes\n            .get(node_id as usize)\n            .ok_or_else(|| anyhow::anyhow!(\"runtime snapshot node does not exist\"))?;\n        let yaml = serde_yaml::to_string(&node.snapshot.config)?;\n        Ok(RuntimeInspectionContent {\n            diff: self\n                .inspection\n                .graph\n                .comparison_parent(node_id)\n                .map(|parent_id| -> anyhow::Result<_> {\n                    Ok(RuntimeInspectionDiff {\n                        parent_id,\n                        hunks: self.inspection.graph.nodes[parent_id as usize]\n                            .snapshot\n                            .diff_yaml_to(&yaml)?,\n                    })\n                })\n                .transpose()?,\n            yaml,\n            logs: self\n                .inspection","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/client/runtime_inspection.rs#L76-L112","documentation":"Thrown by `RuntimeSnapshot::inspection_content` when `node_id` is out of bounds for the current snapshot's config graph (`graph.nodes.get(node_id as usize)` returned None). Node ids are indices into the snapshot graph built during the config pipeline; a valid id in one snapshot may be invalid in another. The library treats this as a caller contract violation rather than silently returning empty content.","triggerScenarios":"Calling `inspect_runtime_node` (via `NyanpasuClient`) with a node_id larger than or equal to `nodes.len()` of the promoted snapshot's graph — typically an id from a previous inspection summary, a hardcoded id, or an id from a different snapshot whose graph has fewer nodes.","commonSituations":"Frontend caches node ids from an old inspection tree; UI navigates to a node that no longer exists after the pipeline shape changed (profiles added/removed change the graph); off-by-one or index confusion between tree position and node id.","solutions":["Always obtain node ids from the current `inspect_runtime()` summary's `nodes` array, never from cached data.","Re-fetch the inspection summary whenever the snapshot changes, then map UI selections to fresh ids.","Handle the error by reloading the whole inspection view (summary + node) instead of retrying the same id.","Validate `node_id < summary.nodes.len()` client-side before calling."],"exampleFix":"// before\nclient.inspect_runtime_node(&summary.snapshot_id, old_node_id).await?;\n// after\nif let Some(node) = summary.nodes.get(old_node_id as usize) {\n    client.inspect_runtime_node(&summary.snapshot_id, node.id).await?;\n} else {\n    // refresh summary and re-resolve the node\n}","handlingStrategy":"validation","validationCode":"if node_id as usize >= summary.nodes.len() {\n    anyhow::bail!(\"node {node_id} not in current inspection ({} nodes)\", summary.nodes.len());\n}","typeGuard":"fn resolve_node<'a>(summary: &'a RuntimeInspection, node_id: u32) -> Option<&'a RuntimeInspectionNode> {\n    summary.nodes.get(node_id as usize)\n}","tryCatchPattern":"match client.inspect_runtime_node(&snapshot_id, node_id).await {\n    Ok(content) => content,\n    Err(e) if e.to_string().contains(\"node does not exist\") => {\n        // reload summary and re-resolve the node id\n        let summary = client.inspect_runtime().await.ok_or_else(|| anyhow!(\"no inspection\"))?;\n        client.inspect_runtime_node(&summary.snapshot_id, summary.root_id).await?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Source all node ids from the current summary's nodes array, never from persisted UI state","Validate node_id against nodes.len() before calling","Rebuild the inspection view whenever the snapshot generation changes","Default to root_id when a previously selected node disappears"],"tags":["rust","index-out-of-range","snapshot","config-inspection"],"backgroundTag":"index-out-of-bounds","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"}