{"record":{"id":"739ecbf80621e2d7","repo":"influxdata/influxdb","slug":"existing-transaction-for-table-should-not-exist","errorCode":null,"errorMessage":"Existing transaction for table should not exist","messagePattern":"Existing transaction for table should not exist","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"influxdb3_catalog/src/catalog/versions/v2/update.rs","lineNumber":1898,"sourceCode":"    /// Get a mutable reference to a [TableTransaction] for the given table name.\n    ///\n    /// If no table transaction exists and the table exists in the current `database_schema`,\n    /// a new [TableTransaction] is created and returned.\n    pub(crate) fn table_transaction(&mut self, name: &str) -> Option<&mut TableTransaction> {\n        if let Some(id) = self.tables.id_for_name(name) {\n            self.tables.get_mut_by_id(&id)\n        } else if let Some(def) = self.database_schema.table_definition(name) {\n            let id = def.table_id;\n            // start a transaction for this table\n            let tx = TableTransaction::new(\n                def.as_ref().clone(),\n                Arc::clone(&self.database_schema),\n                self.columns_per_table_limit,\n                self.storage_mode,\n            );\n            self.tables\n                .insert(tx.table.table_id, tx)\n                .expect(\"Existing transaction for table should not exist\");\n            self.tables.get_mut_by_id(&id)\n        } else {\n            None\n        }\n    }\n\n    pub fn table_or_create(&mut self, table_name: &str) -> Result<TableId> {\n        match self.table_transaction(table_name) {\n            Some(tx) => Ok(tx.table.table_id),\n            None => self.create_table(\n                table_name,\n                create_table_columns::none(),\n                None,\n                FieldFamilyMode::Aware,\n            ),\n        }\n    }\n","sourceCodeStart":1880,"sourceCodeEnd":1916,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/influxdb3_catalog/src/catalog/versions/v2/update.rs#L1880-L1916","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Do not reuse a CatalogBatch after any operation has inserted a table transaction; build a fresh batch per logical operation","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","Verify thread-safety: only one thread should mutate a given CatalogBatch at a time","If hit inside the library, file a bug with the write path that triggered it"],"exampleFix":"// before\nlet batch = CatalogBatch::new(...);\nbatch.table_or_create(\"cpu\")?;\nbatch.table_or_create(\"cpu\")?; // panics: transaction already inserted\n// after\nbatch.table_or_create(\"cpu\")?;\nlet tx = batch.table_tx_or_create(\"cpu\")?; // reuse the existing transaction","handlingStrategy":"validation","validationCode":"// before inserting into a batch, ensure no open transaction exists for the table\nif batch.tables.contains_key(&table_id) {\n    return Err(anyhow!(\"table {} already has an open transaction in this batch\", table_id));\n}","typeGuard":"fn has_open_tx(batch: &CatalogBatch, table_id: TableId) -> bool {\n    batch.tables.contains_key(&table_id)\n}","tryCatchPattern":null,"preventionTips":["Create each table at most once per CatalogBatch","Never reuse or replay a partially mutated batch; build a fresh one per operation","Do not share a &mut CatalogBatch across threads","Keep table mutations through the public transaction APIs only"],"tags":["rust","panic","catalog","invariant"],"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"}