apache/shardingsphere · error · SQLException

Column index out of range: %s

Error message

Column index out of range: %s

What it means

RawResultSetMetaData.getColumnMetaData validates the 1-based column index before delegating metadata lookups (getColumnName, getColumnType, getColumnTypeName, getColumnClassName, etc.) on results backed by raw (non-JDBC-driver) column lists, e.g. federation or locally built metadata. Any index < 1 or > columns.size() throws a plain SQLException 'Column index out of range: %s', mirroring the contract of standard JDBC drivers.

Source

Thrown at infra/executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/execute/result/query/impl/raw/metadata/RawResultSetMetaData.java:198

            case Types.BINARY:
            case Types.VARBINARY:
            case Types.LONGVARBINARY:
                return byte[].class.getName();
            case Types.CHAR:
            case Types.VARCHAR:
            case Types.LONGVARCHAR:
            case Types.NCHAR:
            case Types.NVARCHAR:
            case Types.LONGNVARCHAR:
                return String.class.getName();
            default:
                return Object.class.getName();
        }
    }
    
    private RawQueryResultColumnMetaData getColumnMetaData(final int column) throws SQLException {
        if (column < 1 || column > columns.size()) {
            throw new SQLException(String.format("Column index out of range: %s", column));
        }
        return columns.get(column - 1);
    }
    
    @Override
    public <T> T unwrap(final Class<T> iface) throws SQLException {
        if (isWrapperFor(iface)) {
            return iface.cast(this);
        }
        throw new SQLFeatureNotSupportedException(String.format("`%s` cannot be unwrapped as `%s`", getClass().getName(), iface.getName()));
    }
    
    @Override
    public boolean isWrapperFor(final Class<?> iface) {
        return iface.isInstance(this);
    }
}

View on GitHub (pinned to e952770a21)

Solutions

  1. Use 1-based iteration: for (int i = 1; i <= md.getColumnCount(); i++).
  2. Guard every lookup: if (column >= 1 && column <= md.getColumnCount()) before calling any getXxx(column).
  3. Re-read metadata after re-execution instead of caching column counts across statements.

Example fix

// before
for (int i = 0; i < metaData.getColumnCount(); i++) {
    names.add(metaData.getColumnName(i));
}

// after
for (int i = 1; i <= metaData.getColumnCount(); i++) {
    names.add(metaData.getColumnName(i));
}
Defensive patterns

Strategy: validation

Validate before calling

int n = metaData.getColumnCount();
if (column < 1 || column > n) { throw new IllegalArgumentException("column " + column + " not in 1.." + n); }

Try / catch

try { name = metaData.getColumnName(column); } catch (SQLException e) { /* log and skip column */ }

Prevention

When it happens

Trigger: Iterating metadata with a 0-based loop (for i = 0; i < md.getColumnCount(); i++ then md.getColumnName(i)); calling md.getColumnName(n) where n exceeds the projected column count after ShardingSphere rewrites or merges the query; stale cached column count after a re-executed statement.

Common situations: Off-by-one bugs ported from 0-based APIs; result-set merging (DISTINCT, aggregation, ORDER BY) producing fewer columns than the original SQL; code that assumes the driver tolerates out-of-range probes instead of throwing.

Related errors


AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14). Data as JSON: /api/errors/4e22d6bb5e0f18e3. Report an issue: GitHub.