OtterMind/Chat2DB · error · IllegalArgumentException

Snowflake index requires at least one named column

Error message

Snowflake index requires at least one named column

What it means

Thrown by SnowflakeIndexTypeEnum.buildIndexColumn when, after iterating every TableIndexColumn in tableIndex.getColumnList(), none had a non-blank columnName. The builder appends quoted column names inside the parentheses; if nothing was appended it cannot safely delete the trailing comma, so it fails closed.

Source

Thrown at chat2db-community-server/chat2db-community-plugins/chat2db-community-snowflake/src/main/java/ai/chat2db/plugin/snowflake/enums/type/SnowflakeIndexTypeEnum.java:108

    }

    private String buildIndexColumn(TableIndex tableIndex) {
        StringBuilder script = new StringBuilder();
        script.append("(");
        boolean appended = false;
        for (TableIndexColumn column : tableIndex.getColumnList()) {
            if(StringUtils.isNotBlank(column.getColumnName())) {
                script.append(SnowflakeIdentifierProcessor.INSTANCE.quoteIdentifierAlways(column.getColumnName()));
                if (!StringUtils.isBlank(column.getAscOrDesc()) && !PRIMARY_KEY.equals(this)) {
                    script.append(" ").append(SnowflakeSqlGuards.requireAscOrDesc(column.getAscOrDesc()));
                }
                script.append(",");
                appended = true;
            }
        }
        if (!appended) {
            throw new IllegalArgumentException("Snowflake index requires at least one named column");
        }
        script.deleteCharAt(script.length() - 1);
        script.append(")");
        return script.toString();
    }

    private String buildIndexName(TableIndex tableIndex) {
        if(this.equals(PRIMARY_KEY)){
            return "";
        }else {
            return SnowflakeIdentifierProcessor.INSTANCE.quoteIdentifierAlways(tableIndex.getName());
        }
    }

    public String buildModifyIndex(TableIndex tableIndex) {
        if (EditStatusEnum.DELETE.name().equals(tableIndex.getEditStatus())) {
            return buildDropIndex(tableIndex);
        }

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Ensure tableIndex.getColumnList() contains at least one entry with a non-blank columnName before building the DDL.
  2. Guard in the UI/API to reject index save when no columns are selected.
  3. Skip building (continue) when columnList is empty, mirroring the blank-name/blank-type skip already in the CREATE path.

Example fix

// before
index.setColumnList(Collections.emptyList());
// -> Snowflake index requires at least one named column

// after
List<TableIndexColumn> cols = new ArrayList<>();
TableIndexColumn c = new TableIndexColumn();
c.setColumnName("id");
cols.add(c);
index.setColumnList(cols);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure at least one named column before building the index
boolean hasNamed = tableIndex.getColumnList() != null
    && tableIndex.getColumnList().stream()
        .anyMatch(c -> StringUtils.isNotBlank(c.getColumnName()));
if (!hasNamed) {
    throw new IllegalArgumentException("Index has no named columns");
}

Type guard

static boolean indexHasNamedColumn(TableIndex idx) {
    return idx.getColumnList() != null && idx.getColumnList().stream()
        .anyMatch(c -> StringUtils.isNotBlank(c.getColumnName()));
}

Prevention

When it happens

Trigger: Invoking buildIndexScript (transitively via CREATE TABLE index handling) on a TableIndex whose columnList is empty, or whose every column has a null/blank getColumnName().

Common situations: A frontend sends an index object before the user has added any columns; metadata load produced an index with column references stripped; an expression-only index whose 'column' is stored in a field other than columnName.

Related errors


AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14). Data as JSON: /api/errors/64ee1af4dec08190. Report an issue: GitHub.