apache/cassandra · error · InvalidRequestException

A user type cannot contain counters

Error message

A user type cannot contain counters

What it means

Thrown when ALTER TYPE would add or modify a field whose type is a counter. Counter columns cannot be nested inside user-defined types in Cassandra because counters require special replication and cannot be part of composite or nested structures. The check inspects the prepared field type (`type.isCounter()`) before applying the change.

Solutions

  1. Use a regular counter column on a table instead of nesting it in a UDT
  2. Store counters in a dedicated counter table keyed by the same primary key
  3. Choose a non-counter numeric type (e.g. bigint) in the UDT if approximate snapshots suffice

Example fix

// before
ALTER TYPE ks.stats ADD hits counter;
// after
CREATE TABLE ks.stat_counters (id uuid PRIMARY KEY, hits counter);
Defensive patterns

Strategy: validation

Validate before calling

if (fieldType.trim().equalsIgnoreCase("counter"))
    throw new IllegalArgumentException("Counters cannot be used inside user types; use a counter table column");

Type guard

boolean isCounterField(String cqlFieldType) { return cqlFieldType.trim().equalsIgnoreCase("counter"); }

Try / catch

try { session.execute(alterTypeStmt); }
catch (InvalidQueryException e) {
    if ("A user type cannot contain counters".equals(e.getMessage())) {
        // redesign: use a counter table instead
    } else throw e;
}

Prevention

When it happens

Trigger: Executing `ALTER TYPE ks.typ ADD field counter` or altering an existing field to counter type.

Common situations: Developers assuming counters behave like normal columns everywhere; porting table column definitions into UDTs; ORM/migration generators emitting counter fields inside types.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at src/java/org/apache/cassandra/cql3/statements/schema/AlterTypeStatement.java:143

        @Override
        public void validate(ClientState state)
        {
            super.validate(state);

            // save the query state to use it for guardrails validation in #apply
            this.state = state;
        }

        @Override
        public boolean compatibleWith(ClusterMetadata metadata)
        {
            return metadata.directory.commonSerializationVersion.isAtLeast(Version.V0);
        }

        UserType apply(KeyspaceMetadata keyspace, UserType userType)
        {
            if (type.isCounter())
                throw ire("A user type cannot contain counters");

            if (type.isUDT() && !type.isFrozen())
                throw ire("A user type cannot contain non-frozen UDTs");

            if (userType.fieldPosition(fieldName) >= 0)
            {
                if (!ifFieldNotExists)
                    throw ire("Cannot add field %s to type %s: a field with name %s already exists", fieldName, userType.getCqlTypeName(), fieldName);
                return userType;
            }

            AbstractType<?> fieldType = type.prepare(keyspaceName, keyspace.types).getType();
            if (fieldType.referencesUserType(userType.name))
                throw ire("Cannot add new field %s of type %s to user type %s as it would create a circular reference", fieldName, type, userType.getCqlTypeName());

            Collection<TableMetadata> tablesWithTypeInPartitionKey = findTablesReferencingTypeInPartitionKey(keyspace, userType);
            if (!tablesWithTypeInPartitionKey.isEmpty())
            {

View on GitHub (pinned to 88fd0f6a0e)