apache/cassandra · error · InvalidRequestException

Trigger ' ' already exists

Error message

Trigger '%s' already exists

What it means

A trigger with the given name is already registered on the target table. apply() finds an existing TriggerMetadata and, unless IF NOT EXISTS was specified, throws instead of silently overwriting the existing trigger definition.

Solutions

  1. Add IF NOT EXISTS: CREATE TRIGGER IF NOT EXISTS ... to make the statement idempotent
  2. Drop the existing trigger first: DROP TRIGGER existing_trg ON ks.tbl; then recreate it
  3. Choose a different trigger name

Example fix

// before
CREATE TRIGGER trg ON ks.tbl USING 'com.example.Trig';
// after
CREATE TRIGGER IF NOT EXISTS trg ON ks.tbl USING 'com.example.Trig';
Defensive patterns

Strategy: validation

Validate before calling

const ex = await session.execute("SELECT trigger_name FROM system_schema.triggers WHERE keyspace_name = ? AND table_name = ? AND trigger_name = ?", [ks, table, trigger]);
if (ex.rows.length > 0) console.log('trigger already exists, skipping or dropping first');

Try / catch

try { session.execute(ddl); } catch (e) { if (/already exists/.test(e.message)) { /* add IF NOT EXISTS or DROP TRIGGER then retry */ } else throw e; }

Prevention

When it happens

Trigger: CREATE TRIGGER existing_trg ON ks.tbl USING 'cls' where ks.tbl already has a trigger named existing_trg and no IF NOT EXISTS clause; re-running non-idempotent DDL scripts.

Common situations: Idempotency failures in migration scripts executed twice; divergent environments where one already has the trigger; accidental reuse of a trigger name for a different class.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/f83a6c9f7ce0c517. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/cql3/statements/schema/CreateTriggerStatement.java:100

        Keyspaces schema = metadata.schema.getKeyspaces();
        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);
        if (null == keyspace)
            throw ire("Keyspace '%s' doesn't exist", keyspaceName);

        TableMetadata table = keyspace.getTableOrViewNullable(tableName);
        if (null == table)
            throw ire("Table '%s' doesn't exist", tableName);

        if (table.isView())
            throw ire("Cannot CREATE TRIGGER for a materialized view");

        TriggerMetadata existingTrigger = table.triggers.get(triggerName).orElse(null);
        if (null != existingTrigger)
        {
            if (ifNotExists)
                return schema;

            throw ire("Trigger '%s' already exists", triggerName);
        }

        try
        {
            TriggerExecutor.instance.loadTriggerClass(triggerClass);
        }
        catch (Exception e)
        {
            logger.warn(String.format("Trigger class '%s' couldn't be loaded at apply stage.", triggerClass));
        }

        TableMetadata newTable = table.withSwapped(table.triggers.with(TriggerMetadata.create(triggerName, triggerClass)));
        return schema.withAddedOrUpdated(keyspace.withSwapped(keyspace.tables.withSwapped(newTable)));
    }

    SchemaChange schemaChangeEvent(KeyspacesDiff diff)
    {
        return new SchemaChange(Change.UPDATED, Target.TABLE, keyspaceName, tableName);

View on GitHub (pinned to 88fd0f6a0e)