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
- Use 1-based iteration: for (int i = 1; i <= md.getColumnCount(); i++).
- Guard every lookup: if (column >= 1 && column <= md.getColumnCount()) before calling any getXxx(column).
- 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
- Always treat JDBC metadata indexes as 1-based in loops and lookups.
- Never cache column counts across re-executions of a statement.
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.