{"record":{"id":"f5b67677c845ca27","repo":"apache/cassandra","slug":"cannot-create-materialized-view-s-for-base-tabl","errorCode":null,"errorMessage":"Cannot create materialized view '%s' for base table '%s' with gc_grace_seconds of 0, since this value is used to TTL undelivered updates. Setting gc_grace_seconds too low might cause undelivered updates to expire before being replayed.","messagePattern":"Cannot create materialized view '(.+?)' for base table '(.+?)' with gc_grace_seconds of 0, since this value is used to TTL undelivered updates\\. Setting gc_grace_seconds too low might cause undelivered updates to expire before being replayed\\.","errorType":"validation","errorClass":"InvalidRequestException","httpStatus":null,"severity":"error","filePath":"src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java","lineNumber":190,"sourceCode":"        if (table.isCounter())\n            throw ire(\"Materialized views are not supported on counter tables\");\n\n        if (table.isView())\n            throw ire(\"Materialized views cannot be created against other materialized views\");\n\n        // Guardrails on table properties\n        Guardrails.tableProperties.guard(attrs.updatedProperties(), attrs::removeProperty, state);\n\n        // Guardrail to limit number of mvs per table\n        Iterable<ViewMetadata> tableViews = keyspace.views.forTable(table.id);\n        Guardrails.materializedViewsPerTable.guard(Iterables.size(tableViews) + 1,\n                                                   String.format(\"%s on table %s\", viewName, table.name),\n                                                   false,\n                                                   state);\n\n        if (table.params.gcGraceSeconds == 0)\n        {\n            throw ire(\"Cannot create materialized view '%s' for base table \" +\n                      \"'%s' with gc_grace_seconds of 0, since this value is \" +\n                      \"used to TTL undelivered updates. Setting gc_grace_seconds\" +\n                      \" too low might cause undelivered updates to expire \" +\n                      \"before being replayed.\",\n                      viewName, tableName);\n        }\n\n        if (table.params.pendingDrop)\n            throw ire(\"Cannot create materialized view '%s' for base table \" +\n                      \"'%s' as it is being dropped.\",\n                      viewName, tableName);\n\n        /*\n         * Process SELECT clause\n         */\n\n        Set<ColumnIdentifier> selectedColumns = new HashSet<>();\n","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/apache/cassandra/blob/88fd0f6a0eaed8943f05ac9e8f947882b8ddc8f1/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java#L172-L208","documentation":"gc_grace_seconds controls how long deleted/tombstoned data is retained so MV update batches can still be delivered to the view. If the base table sets it to 0, undelivered MV updates can be garbage-collected before replay, silently losing view rows. Cassandra refuses to create such a view because it would be incorrect.","triggerScenarios":"CREATE MATERIALIZED VIEW on a base table whose table params have gc_grace_seconds == 0 (commonly set with WITH gc_grace_seconds = 0 in CREATE TABLE or ALTER TABLE).","commonSituations":"Tables tuned for pure time-series workloads with no deletes where operators set gc_grace_seconds=0 for compaction performance; users then add an MV on top and hit this wall.","solutions":["ALTER TABLE <base> WITH gc_grace_seconds = 864000 (default) or another non-zero value before creating the view","If zero is truly required, do not use materialized views on that table; maintain denormalized data in application code","Re-create the base table with a proper gc_grace_seconds if the property was set by mistake"],"exampleFix":"// before\nCREATE TABLE base (...) WITH gc_grace_seconds = 0;\nCREATE MATERIALIZED VIEW mv AS SELECT ... FROM base ...;\n// after\nALTER TABLE base WITH gc_grace_seconds = 864000;\nCREATE MATERIALIZED VIEW mv AS SELECT ... FROM base ...;","handlingStrategy":"validation","validationCode":"long ggs = baseTable.params.gcGraceSeconds; if (ggs == 0) throw new IllegalStateException(\"Set gc_grace_seconds > 0 before creating an MV on \" + baseTable);","typeGuard":null,"tryCatchPattern":"try { session.execute(\"ALTER TABLE base WITH gc_grace_seconds = 864000\"); session.execute(createMvStmt); } catch (InvalidQueryException e) { /* inspect gc_grace_seconds message */ }","preventionTips":["Keep gc_grace_seconds at its default (864000) on any table that has MVs","Document that gc_grace_seconds=0 tuning is incompatible with MVs","Check table params in system_schema.tables before provisioning MVs"],"tags":["cql","materialized-view","gc-grace-seconds","tombstones"],"backgroundTag":"invalid-config-value","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"}