influxdata/influxdb · error

table should exist by id

Error message

table should exist by id

What it means

table_tx_or_create creates (or looks up) a table and then expects the resulting id to be present in self.tables. Since table_or_create is expected to have inserted a TableTransaction for that id, get_mut_by_id returning None means the insert path skipped the transaction (e.g. the table already existed and the else branch returned None upstream of a broken assumption) or the map was mutated inconsistently.

Solutions

  1. Prefer obtaining the transaction from the same call: use table_tx_or_create rather than mixing table_or_create with separate map lookups
  2. Ensure every path that returns a table_id also inserts a TableTransaction keyed by that id in the same batch
  3. Check the library version for a fix to the existing-table branch of table_or_create and upgrade
  4. If reproducible on the latest version, file a bug with the failing call sequence

Example fix

// before
let id = batch.table_or_create("cpu")?;
let tx = batch.get_mut_by_id(&id).expect("table should exist by id");
// after
let tx = batch.table_tx_or_create("cpu")?; // single source of truth
Defensive patterns

Strategy: validation

Validate before calling

// confirm the table exists in the schema before requesting a transaction
if db_schema.table_id(table_name).is_none() && !batch.will_create_table(table_name) {
    return Err(anyhow!("table {} unknown and not scheduled for creation", table_name));
}

Type guard

fn tx_for<'a>(batch: &'a mut CatalogBatch, id: &TableId) -> Option<&'a mut TableTransaction> {
    batch.tables.get_mut_by_id(id)
}

Try / catch

let Some(tx) = batch.tables.get_mut_by_id(&id) else {
    return Err(anyhow!("no table transaction for id {id:?}; create it first"));
};

Prevention

When it happens

Trigger: Calling table_tx_or_create when table_or_create returned an existing table's id without inserting a TableTransaction for it into self.tables, so the subsequent get_mut_by_id finds nothing.

Common situations: Write paths that touch an already-existing table but expect an open table transaction; batches built through mixed legacy/new APIs where the existing table was never registered as a transaction.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of influxdata/influxdb@06200ef96b (2026-09-19). Data as JSON: /api/errors/7b42f56779d306c0. Report an issue: GitHub.

Appendix: source

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

    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,
            ),
        }
    }

    pub fn table_tx_or_create(&mut self, table_name: &str) -> Result<&mut TableTransaction> {
        let id = self.table_or_create(table_name)?;
        Ok(self
            .tables
            .get_mut_by_id(&id)
            .expect("table should exist by id"))
    }

    pub fn column_or_create(
        &mut self,
        table_name: &str,
        column_name: &str,
        column_type: InfluxColumnType,
    ) -> Result<ColumnDefinition> {
        let Some(table_tx) = self.table_transaction(table_name) else {
            return Err(CatalogError::NotFound(table_name.to_string()));
        };
        match table_tx.table.column_definition(column_name) {
            Some(def) if def.column_type() == column_type => Ok(def),
            Some(def) => Err(CatalogError::InvalidColumnType {
                column_name: Arc::clone(&def.name()),
                expected: def.column_type(),
                got: column_type,
            }),

View on GitHub (pinned to 06200ef96b)