influxdata/influxdb · critical
existing node should update
Error message
existing node should update
What it means
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.
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.
Example fix
// before
self.nodes
.update(node_batch.node_catalog_id, node)
.expect("existing node should update");
// after
self.nodes
.update(node_batch.node_catalog_id, node)
.unwrap_or_else(|| panic!(
"node {} present in branch but missing from repository",
node_batch.node_catalog_id
)); Defensive patterns
Strategy: try-catch
Validate before calling
// before triggering node create replay
if nodes.get_by_id(&node_catalog_id).is_some() {
// route to update path only if still present at update time
} Type guard
fn node_present(nodes: &Nodes, id: NodeId) -> bool { nodes.get_by_id(&id).is_some() } Try / catch
// .expect() panics cannot be caught in Rust; guard the call instead
match self.nodes.update(node_batch.node_catalog_id, node) {
Some(_) => true,
None => panic!("existing node should update: {}", node_batch.node_catalog_id),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- existing database should be updated
- there should not already be a node
- token to be updated
- {0}
- auto field family exists
AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19).
Data as JSON: /api/errors/b335e0642d2faa3b.
Report an issue: GitHub.
Appendix: source
Thrown at influxdb3_catalog/src/catalog/versions/v2.rs:1715
if let Some(mut node) = self.nodes.get_by_name(node_id) {
// Allow re-registration of stopped nodes with different instance_id
if !matches!(node.state, NodeState::Stopped { .. })
&& &node.instance_id != instance_id
{
return Err(CatalogError::InvalidNodeRegistration);
}
let n = Arc::make_mut(&mut node);
n.instance_id = Arc::clone(instance_id);
n.mode = mode.clone();
n.core_count = *core_count;
n.state = NodeState::Running {
registered_time_ns: *registered_time_ns,
};
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()));
self.nodes
.update(node_batch.node_catalog_id, node)
.expect("existing node should update");
} else {
let new_node = Arc::new(NodeDefinition {
node_id: Arc::clone(node_id),
node_catalog_id: node_batch.node_catalog_id,
instance_id: Arc::clone(instance_id),
mode: mode.clone(),
core_count: *core_count,
state: NodeState::Running {
registered_time_ns: *registered_time_ns,
},
conn_info: conn_info.as_ref().map(|s| Arc::from(s.as_str())),
cli_params: cli_params.as_ref().map(|s| Arc::from(s.as_str())),
row_delete_predicate_version: *row_delete_predicate_version,
});
self.nodes
.insert(node_batch.node_catalog_id, new_node)
.expect("there should not already be a node");
}View on GitHub (pinned to 06200ef96b)