apache/cassandra · error

All indexed columns should be included into the column…

Error message

All indexed columns should be included into the column slice, missing: ${column}

What it means

Operation.localSatisfiedBy evaluates each indexed column of a column slice against a row. When allowMissingColumns is false, any indexed column whose value cannot be resolved (null from ColumnIndex.getValueOf) is treated as a broken plan invariant and throws IllegalStateException. The library assumes query planning guaranteed every indexed column is present in the slice being evaluated.

Solutions

  1. Run a full repair and rebuild the SASI index so all SSTables/rows contain the indexed column values
  2. Check schema agreement (SELECT * FROM system_schema.columns) and ensure all nodes agree the indexed column exists
  3. Ensure rows are rewritten (UPDATE setting the column) if pre-existing data predates the column addition
  4. If the query is intentionally tolerant of missing columns, verify the allowMissingColumns flag is set by the planner path you use

Example fix

// before
cqlsh> SELECT * FROM ks.tbl WHERE age = 30 ALLOW FILTERING; // rows missing 'age' crash SASI plan eval
// after
cqlsh> ALTER TABLE ks.tbl DROP INDEX age_idx;
cqlsh> ALTER TABLE ks.tbl ADD age int; -- ensure column exists before indexing
cqlsh> CREATE CUSTOM INDEX age_idx ON ks.tbl (age) USING 'org.apache.cassandra.index.sasi.SASIIndex';
Defensive patterns

Strategy: try-catch

Validate before calling

cqlsh> SELECT column_name FROM system_schema.columns WHERE keyspace_name='ks' AND table_name='tbl';
-- confirm the indexed column exists and is populated in all rows before querying

Try / catch

try {
    session.execute("SELECT * FROM ks.tbl WHERE age = ?", 30);
} catch (IllegalStateException e) {
    // log plan inconsistency; trigger repair/index rebuild
    scheduler.rebuildIndex("ks", "tbl");
}

Prevention

When it happens

Trigger: Executing a SASI query whose plan includes multiple indexed columns where a row (or the static row for Kind.STATIC columns) is missing one of the indexed column values; the controller built the Operation without allowing missing columns, then satisfiedBy() reaches a row lacking a column value.

Common situations: Rows written before a column was added to the schema but after the SASI index was created; queries mixing regular and static indexed columns; legacy SSTables that don't contain the indexed column; schema disagreements between nodes.

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 apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/797cecf790ddcfd6. Report an issue: GitHub.

Appendix: source

Thrown at src/java/org/apache/cassandra/index/sasi/plan/Operation.java:223

    private boolean localSatisfiedBy(Unfiltered currentCluster, Row staticRow, boolean allowMissingColumns)
    {
        if (currentCluster == null || !currentCluster.isRow())
            return false;

        final long now = FBUtilities.nowInSeconds();
        boolean result = false;
        int idx = 0;

        for (ColumnMetadata column : expressions.keySet())
        {
            if (column.kind == Kind.PARTITION_KEY)
                continue;

            ByteBuffer value = ColumnIndex.getValueOf(column, column.kind == Kind.STATIC ? staticRow : (Row) currentCluster, now);
            boolean isMissingColumn = value == null;

            if (!allowMissingColumns && isMissingColumn)
                throw new IllegalStateException("All indexed columns should be included into the column slice, missing: " + column);

            boolean isMatch = false;
            // If there is a column with multiple expressions that effectively means an OR
            // e.g. comment = 'x y z' could be split into 'comment' EQ 'x', 'comment' EQ 'y', 'comment' EQ 'z'
            // by analyzer, in situation like that we only need to check if at least one of expressions matches,
            // and there is no hit on the NOT_EQ (if any) which are always at the end of the filter list.
            // Loop always starts from the end of the list, which makes it possible to break after the last
            // NOT_EQ condition on first EQ/RANGE condition satisfied, instead of checking every
            // single expression in the column filter list.
            List<Expression> filters = expressions.get(column);
            for (int i = filters.size() - 1; i >= 0; i--)
            {
                Expression expression = filters.get(i);
                isMatch = !isMissingColumn && expression.isSatisfiedBy(value);
                if (expression.getOp() == Op.NOT_EQ)
                {
                    // since this is NOT_EQ operation we have to
                    // inverse match flag (to check against other expressions),

View on GitHub (pinned to 88fd0f6a0e)