{"record":{"id":"b335e0642d2faa3b","repo":"influxdata/influxdb","slug":"existing-node-should-update","errorCode":null,"errorMessage":"existing node should update","messagePattern":"existing node should update","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"influxdb3_catalog/src/catalog/versions/v2.rs","lineNumber":1715,"sourceCode":"                    if let Some(mut node) = self.nodes.get_by_name(node_id) {\n                        // Allow re-registration of stopped nodes with different instance_id\n                        if !matches!(node.state, NodeState::Stopped { .. })\n                            && &node.instance_id != instance_id\n                        {\n                            return Err(CatalogError::InvalidNodeRegistration);\n                        }\n                        let n = Arc::make_mut(&mut node);\n                        n.instance_id = Arc::clone(instance_id);\n                        n.mode = mode.clone();\n                        n.core_count = *core_count;\n                        n.state = NodeState::Running {\n                            registered_time_ns: *registered_time_ns,\n                        };\n                        n.conn_info = conn_info.as_ref().map(|s| Arc::from(s.as_str()));\n                        n.cli_params = cli_params.as_ref().map(|s| Arc::from(s.as_str()));\n                        self.nodes\n                            .update(node_batch.node_catalog_id, node)\n                            .expect(\"existing node should update\");\n                    } else {\n                        let new_node = Arc::new(NodeDefinition {\n                            node_id: Arc::clone(node_id),\n                            node_catalog_id: node_batch.node_catalog_id,\n                            instance_id: Arc::clone(instance_id),\n                            mode: mode.clone(),\n                            core_count: *core_count,\n                            state: NodeState::Running {\n                                registered_time_ns: *registered_time_ns,\n                            },\n                            conn_info: conn_info.as_ref().map(|s| Arc::from(s.as_str())),\n                            cli_params: cli_params.as_ref().map(|s| Arc::from(s.as_str())),\n                            row_delete_predicate_version: *row_delete_predicate_version,\n                        });\n                        self.nodes\n                            .insert(node_batch.node_catalog_id, new_node)\n                            .expect(\"there should not already be a node\");\n                    }","sourceCodeStart":1697,"sourceCodeEnd":1733,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/influxdb3_catalog/src/catalog/versions/v2.rs#L1697-L1733","documentation":"This is a panic via `.expect()` raised during replay/apply of a NodeCatalogOp::CreateNode batch in the v2 catalog. The code took the branch indicating the node already exists and attempted an in-place update in the `nodes` repository, but `update()` returned None, meaning the repository no longer holds an entry for that node_catalog_id. It signals an internal consistency violation between the branch decision logic and the repository state, so the process panics rather than silently corrupting the catalog.","triggerScenarios":"Applying a CreateNode node batch where the node_catalog_id was found in an earlier lookup (e.g. via get_by_id) but is missing from `self.nodes` when `update()` runs — only possible if the repository was mutated concurrently or the lookup/update pair is inconsistent.","commonSituations":"Corrupted or partially written catalog WAL/snapshot replay, concurrent catalog mutations racing on the same node id, or a bug introduced while modifying the node replay code path.","solutions":["Check for concurrent writers to the catalog; ensure only one process/thread applies catalog batches.","Inspect the catalog snapshot/WAL for the node_catalog_id to detect corruption; restore from backup if entries are missing.","Review recent changes to the CreateNode replay branch; the lookup and update must target the same repository state.","If reproducible, file an issue with the catalog logs; this should be unreachable in normal operation."],"exampleFix":"// before\nself.nodes\n    .update(node_batch.node_catalog_id, node)\n    .expect(\"existing node should update\");\n// after\nself.nodes\n    .update(node_batch.node_catalog_id, node)\n    .unwrap_or_else(|| panic!(\n        \"node {} present in branch but missing from repository\",\n        node_batch.node_catalog_id\n    ));","handlingStrategy":"try-catch","validationCode":"// before triggering node create replay\nif nodes.get_by_id(&node_catalog_id).is_some() {\n    // route to update path only if still present at update time\n}","typeGuard":"fn node_present(nodes: &Nodes, id: NodeId) -> bool { nodes.get_by_id(&id).is_some() }","tryCatchPattern":"// .expect() panics cannot be caught in Rust; guard the call instead\nmatch self.nodes.update(node_batch.node_catalog_id, node) {\n    Some(_) => true,\n    None => panic!(\"existing node should update: {}\", node_batch.node_catalog_id),\n}","preventionTips":["Keep catalog batch application single-threaded/single-writer","Verify WAL and snapshot do not overlap during replay","Add integrity checks comparing node batch ids against repository contents in tests","Monitor for panics in catalog replay logs"],"tags":["panic","catalog","inconsistent-state","replay"],"backgroundTag":"internal-invariant-violation","analyzedSha":"06200ef96ba82c5f6727e5038a83af8e722c6875","analyzedAt":"2026-09-19T12:55:30.003Z","contentChangedAt":"2026-09-19T12:55:30.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}