influxdata/influxdb · error

no duplicate tag

Error message

no duplicate tag

What it means

This is a panic raised by an `.expect("no duplicate tag")` on `self.table.tag_columns.insert(id, tag_col)` while adding a tag column to a table in the InfluxDB3 catalog (influxdb3_catalog::catalog::versions::v2::update). The insert returns an error only if the map already holds a column under the same table-wide unique id, which the update transaction logic assumes can never happen. The library throws it because a violation means the in-memory TableDefinition has diverged from its log — i.e. catalog corruption — rather than a recoverable condition.

Solutions

  1. Check whether the catalog batch was applied more than once (look for duplicated ops in the write-ahead log / sequence numbers) and remove the duplicate replay.
  2. Verify the catalog file or snapshot is not corrupted or hand-edited; restore from a known-good backup.
  3. Ensure the column id comes from next_legacy_column_id() and that no other code path inserts into tag_columns with a manually chosen id.
  4. If reproducible, capture the failing catalog batch and file an issue with the influxdb3 catalog maintainers — this is an internal invariant failure, not user input validation.

Example fix

// before
self.table.tag_columns.insert(id, Arc::clone(&tag_col)).expect("no duplicate tag");
// after
if self.table.tag_columns.contains_key(&id) {
    return Err(Error::DuplicateTagName { table: self.table.name.clone(), tag: tag.into() });
}
self.table.tag_columns.insert(id, Arc::clone(&tag_col)).expect("no duplicate tag");
Defensive patterns

Strategy: validation

Validate before calling

// Before replaying/applying a batch, check the tag isn't already a column
if table_def.columns().any(|c| c.name() == tag_name) {
    return Err("tag column already exists in table; skip duplicate batch op");
}

Type guard

fn tag_exists(table: &TableDefinition, id: &TableId) -> bool { table.tag_columns.contains_key(id) }

Prevention

When it happens

Trigger: Calling the add-tag-column path of a TableTransaction (e.g. add_tag_column) when the generated table-unique column id (from next_legacy_column_id) or the tag name already exists in table.tag_columns. This can happen if the same catalog batch is applied twice (replay of a non-idempotent update) or if id allocation is skipped/duplicated in custom catalog code.

Common situations: Catalog snapshot/compaction bugs where a batch is replayed on top of an already-applied state; restoring a catalog file into a newer/older schema version; hand-edited or partially-written catalog protobuf files; concurrent writers bypassing the catalog transaction lock.

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/47fa46044808d060. Report an issue: GitHub.

Appendix: source

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

                NUM_TAG_COLUMNS_LIMIT,
            ));
        }

        self.check_columns_limit()?;
        self.table.check_name(tag.as_ref())?;

        let id = self.table.tag_columns.next_id();
        // Only increment if id is less than MAX.
        if id < TagId::MAX {
            self.table.tag_columns.set_next_id(id.next())
        }
        let col_id = self.next_legacy_column_id()?;
        let tag_col = Arc::new(TagColumn::new(id, col_id, tag.as_ref()));

        self.table
            .tag_columns
            .insert(id, Arc::clone(&tag_col))
            .expect("no duplicate tag");

        let col_def = ColumnDefinition::Tag(Arc::clone(&tag_col));
        self.table
            .columns
            .insert(col_def.clone())
            .expect("no duplicate column");

        self.column_definitions.push(col_def.into());

        Ok(tag_col)
    }

    pub(crate) fn add_time(&mut self) -> Result<Arc<TimestampColumn>> {
        if self.table.timestamp_column.is_some() {
            return Err(CatalogError::DuplicateColumn {
                name: TIME_COLUMN_NAME.into(),
                existing: InfluxColumnType::Timestamp,
            });

View on GitHub (pinned to 06200ef96b)