{"record":{"id":"042a69d340fd0459","repo":"apache/cassandra","slug":"cannot-create-trigger-for-a-materialized-view","errorCode":null,"errorMessage":"Cannot CREATE TRIGGER for a materialized view","messagePattern":"Cannot CREATE TRIGGER for a materialized view","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/CreateTriggerStatement.java","lineNumber":92,"sourceCode":"    public boolean compatibleWith(ClusterMetadata metadata)\n    {\n        return metadata.directory.commonSerializationVersion.isAtLeast(Version.V0);\n    }\n\n    @Override\n    public Keyspaces apply(ClusterMetadata metadata)\n    {\n        Keyspaces schema = metadata.schema.getKeyspaces();\n        KeyspaceMetadata keyspace = schema.getNullable(keyspaceName);\n        if (null == keyspace)\n            throw ire(\"Keyspace '%s' doesn't exist\", keyspaceName);\n\n        TableMetadata table = keyspace.getTableOrViewNullable(tableName);\n        if (null == table)\n            throw ire(\"Table '%s' doesn't exist\", tableName);\n\n        if (table.isView())\n            throw ire(\"Cannot CREATE TRIGGER for a materialized view\");\n\n        TriggerMetadata existingTrigger = table.triggers.get(triggerName).orElse(null);\n        if (null != existingTrigger)\n        {\n            if (ifNotExists)\n                return schema;\n\n            throw ire(\"Trigger '%s' already exists\", triggerName);\n        }\n\n        try\n        {\n            TriggerExecutor.instance.loadTriggerClass(triggerClass);\n        }\n        catch (Exception e)\n        {\n            logger.warn(String.format(\"Trigger class '%s' couldn't be loaded at apply stage.\", triggerClass));\n        }","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/CreateTriggerStatement.java#L74-L110","documentation":"Thrown while applying CREATE TRIGGER when the target relation resolved via getTableOrViewNullable() is a materialized view rather than a base table. Triggers may only be attached to base tables; views are maintained automatically by the system, so the statement is rejected.","triggerScenarios":"CREATE TRIGGER trg ON ks.some_materialized_view USING 'cls'; — the name resolves to a view rather than a base table.","commonSituations":"View and base table sharing similar names and the wrong one was picked; refactoring where a table was replaced by a materialized view while trigger DDL still targets the old name.","solutions":["Target the base table that backs the materialized view instead","Drop the trigger DDL if triggers on the view were never valid","Rename or disambiguate so the statement clearly references the base table"],"exampleFix":"// before\nCREATE TRIGGER trg ON ks.orders_by_day USING 'com.example.Trig'; -- materialized view\n// after\nCREATE TRIGGER trg ON ks.orders USING 'com.example.Trig'; -- base table","handlingStrategy":"validation","validationCode":"const v = await session.execute(\"SELECT table_name FROM system_schema.views WHERE keyspace_name = ? AND view_name = ?\", [ks, table]);\nif (v.rows.length > 0) throw new Error(`${ks}.${table} is a materialized view; triggers are not supported`);","typeGuard":null,"tryCatchPattern":"try { session.execute(triggerDdl); } catch (e) { if (/materialized view/.test(e.message)) { /* retarget the base table */ } else throw e; }","preventionTips":["Resolve names against system_schema.views before attaching triggers","Keep distinct naming conventions for tables and views","Document that triggers apply only to base tables"],"tags":["cql","trigger","materialized-view"],"backgroundTag":"unsupported-operation","analyzedSha":"88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1","analyzedAt":"2026-09-10T07:29:22.284Z","contentChangedAt":"2026-09-10T07:29:22.284Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}