influxdata/influxdb · error

Existing transaction for table should not exist

Error message

Existing transaction for table should not exist

What it means

This is a panic from an .expect() on the result of inserting a TableTransaction into the in-flight batch's tables map. The library asserts that no transaction for the same table_id already exists in the map; if insert() reports a previous entry, the batch bookkeeping invariant (one open transaction per table per batch) has been violated. It indicates a logic bug in batch/table mutation paths, not user error.

Solutions

  1. Do not reuse a CatalogBatch after any operation has inserted a table transaction; build a fresh batch per logical operation
  2. Check that all write paths go through the public API once per table per batch; remove duplicate table_or_create/table_tx_or_create calls for the same table
  3. Verify thread-safety: only one thread should mutate a given CatalogBatch at a time
  4. If hit inside the library, file a bug with the write path that triggered it

Example fix

// before
let batch = CatalogBatch::new(...);
batch.table_or_create("cpu")?;
batch.table_or_create("cpu")?; // panics: transaction already inserted
// after
batch.table_or_create("cpu")?;
let tx = batch.table_tx_or_create("cpu")?; // reuse the existing transaction
Defensive patterns

Strategy: validation

Validate before calling

// before inserting into a batch, ensure no open transaction exists for the table
if batch.tables.contains_key(&table_id) {
    return Err(anyhow!("table {} already has an open transaction in this batch", table_id));
}

Type guard

fn has_open_tx(batch: &CatalogBatch, table_id: TableId) -> bool {
    batch.tables.contains_key(&table_id)
}

Prevention

When it happens

Trigger: Calling table-level create/alter methods on the same CatalogBatch/TableBatch twice such that table_or_create returns a table_id already inserted into self.tables — e.g. creating a table, then re-entering the code path that inserts a transaction for the same table_id within one batch.

Common situations: Reused builder/batch objects after a failed operation; concurrent mutation of a CatalogBatch from multiple threads; plugin or extension code that calls table creation APIs twice with the same table name in one write 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/739ecbf80621e2d7. Report an issue: GitHub.

Appendix: source

Thrown at influxdb3_catalog/src/catalog/versions/v2/update.rs:1898

    /// Get a mutable reference to a [TableTransaction] for the given table name.
    ///
    /// If no table transaction exists and the table exists in the current `database_schema`,
    /// a new [TableTransaction] is created and returned.
    pub(crate) fn table_transaction(&mut self, name: &str) -> Option<&mut TableTransaction> {
        if let Some(id) = self.tables.id_for_name(name) {
            self.tables.get_mut_by_id(&id)
        } else if let Some(def) = self.database_schema.table_definition(name) {
            let id = def.table_id;
            // start a transaction for this table
            let tx = TableTransaction::new(
                def.as_ref().clone(),
                Arc::clone(&self.database_schema),
                self.columns_per_table_limit,
                self.storage_mode,
            );
            self.tables
                .insert(tx.table.table_id, tx)
                .expect("Existing transaction for table should not exist");
            self.tables.get_mut_by_id(&id)
        } else {
            None
        }
    }

    pub fn table_or_create(&mut self, table_name: &str) -> Result<TableId> {
        match self.table_transaction(table_name) {
            Some(tx) => Ok(tx.table.table_id),
            None => self.create_table(
                table_name,
                create_table_columns::none(),
                None,
                FieldFamilyMode::Aware,
            ),
        }
    }

View on GitHub (pinned to 06200ef96b)