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

  1. Check for concurrent writers to the catalog; ensure only one process/thread applies catalog batches.
  2. Inspect the catalog snapshot/WAL for the node_catalog_id to detect corruption; restore from backup if entries are missing.
  3. Review recent changes to the CreateNode replay branch; the lookup and update must target the same repository state.
  4. 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

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


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)