{"record":{"id":"4b7aaa3509557dce","repo":"influxdata/influxdb","slug":"there-should-be-a-node-to-update","errorCode":null,"errorMessage":"there should be a node to update","messagePattern":"there should be a node to update","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"influxdb3_catalog/src/catalog/versions/v2.rs","lineNumber":1746,"sourceCode":"                            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                    }\n                    true\n                }\n                NodeCatalogOp::StopNode(StopNodeLog {\n                    stopped_time_ns, ..\n                }) => match self.nodes.get_by_id(&node_batch.node_catalog_id) {\n                    Some(mut new_node) => {\n                        let n = Arc::make_mut(&mut new_node);\n                        n.state = NodeState::Stopped {\n                            stopped_time_ns: *stopped_time_ns,\n                        };\n                        self.nodes\n                            .update(node_batch.node_catalog_id, new_node)\n                            .expect(\"there should be a node to update\");\n                        true\n                    }\n                    None => {\n                        warn!(\n                            node_id = %&node_batch.node_catalog_id,\n                            \"cannot find node id, skipping stop operation\"\n                        );\n                        false\n                    }\n                },\n            };\n        }\n        Ok(updated)\n    }\n\n    fn apply_restore_batch(\n        &mut self,\n        restore_batch: &log::RestoreBatch,","sourceCodeStart":1728,"sourceCodeEnd":1764,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/influxdb3_catalog/src/catalog/versions/v2.rs#L1728-L1764","documentation":"A panic from `.expect()` when applying NodeCatalogOp::StopNode. The match arm only runs after `get_by_id` returned Some, but `update()` then failed to find the node, so the repository state changed between the get and the update. This indicates an inconsistency that the catalog treats as unrecoverable for that operation.","triggerScenarios":"StopNode applied for a node_catalog_id present at the get_by_id check but absent at update time — concurrent removal of the node or a race between replay threads.","commonSituations":"Concurrent StopNode and node deletion in an Enterprise multi-node deployment, or manual tampering with the catalog repository during replay.","solutions":["Ensure catalog mutations are serialized (single writer) so get-then-update pairs are atomic.","Check whether another code path deletes nodes while StopNode batches are being applied.","Restore the node entry or replay the catalog from a consistent snapshot.","If a benign race, convert to the existing None-arm behavior (warn and skip) instead of panicking."],"exampleFix":"// before\nself.nodes\n    .update(node_batch.node_catalog_id, new_node)\n    .expect(\"there should be a node to update\");\n// after\nmatch self.nodes.update(node_batch.node_catalog_id, new_node) {\n    Some(_) => true,\n    None => {\n        warn!(node_id = %node_batch.node_catalog_id, \"node vanished before stop; skipping\");\n        false\n    }\n}","handlingStrategy":"validation","validationCode":"if nodes.get_by_id(&node_batch.node_catalog_id).is_none() {\n    // skip stop op or warn, mirroring the existing None arm\n}","typeGuard":"fn can_stop(nodes: &Nodes, id: NodeId) -> bool { nodes.get_by_id(&id).is_some() }","tryCatchPattern":null,"preventionTips":["Serialize get-then-update pairs on the node repository","Do not delete nodes concurrently with StopNode batches","Fall back to the warn-and-skip path instead of panicking on races","Watch multi-node deployments for simultaneous stop/delete ops"],"tags":["panic","catalog","race-condition","stop-node"],"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"}