tursodatabase/turso · error · SQLException

column name {columnLabel} not found

Error message

column name {columnLabel} not found

What it means

findColumn() throws 'column name <label> not found' after scanning resultSet.getColumnNames() without a match. The match is exact and case-sensitive (String.equals), so only the precise label the query produced - alias included - is accepted.

Source

Thrown at bindings/java/src/main/java/tech/turso/jdbc4/JDBC4ResultSet.java:443

  @Override
  @SkipNullableCheck
  public Object getObject(String columnLabel) throws SQLException {
    return getObject(findColumn(columnLabel));
  }

  @Override
  public int findColumn(String columnLabel) throws SQLException {
    if (columnLabel == null || columnLabel.isEmpty()) {
      throw new SQLException("column name not found");
    }

    final String[] columnNames = resultSet.getColumnNames();
    for (int i = 0; i < columnNames.length; i++) {
      if (columnNames[i].equals(columnLabel)) {
        return i + 1;
      }
    }
    throw new SQLException("column name " + columnLabel + " not found");
  }

  @Override
  @SkipNullableCheck
  public Reader getCharacterStream(int columnIndex) throws SQLException {
    final Object result = resultSet.get(columnIndex);
    wasNull = result == null;
    if (result == null) {
      return null;
    }
    return wrapTypeConversion(() -> new StringReader((String) result));
  }

  @Override
  @Nullable
  public Reader getCharacterStream(String columnLabel) throws SQLException {
    return getCharacterStream(findColumn(columnLabel));
  }

View on GitHub (pinned to bad083fafb)

Solutions

  1. Use the exact label the query defines, including the AS alias (SELECT COUNT(*) AS cnt -> 'cnt')
  2. Alias every expression in the SELECT list so labels are stable and queryable
  3. Enumerate rs.getMetaData().getColumnLabel(i) for i in 1..getColumnCount() at debug level to see the real labels
  4. Trim and constant-case labels on both the SQL and Java sides

Example fix

// before
int n = rs.getInt("count"); // query was SELECT COUNT(*) AS cnt

// after
int n = rs.getInt("cnt");
// or make both sides explicit:
// SELECT COUNT(*) AS count_total ...  ->  rs.getInt("count_total")
Defensive patterns

Strategy: validation

Validate before calling

// Verify the label exists (exact, case-sensitive match) before reading
java.sql.ResultSetMetaData md = rs.getMetaData();
boolean found = false;
for (int i = 1; i <= md.getColumnCount(); i++) {
  if (md.getColumnLabel(i).equals(label)) { found = true; break; }
}
if (!found) {
  throw new IllegalArgumentException("unknown label '" + label + "'; actual: "
      + labelsOf(md));
}

Type guard

static boolean hasColumn(java.sql.ResultSet rs, String label) throws SQLException {
  if (label == null || label.isEmpty()) return false;
  java.sql.ResultSetMetaData md = rs.getMetaData();
  for (int i = 1; i <= md.getColumnCount(); i++) {
    if (label.equals(md.getColumnLabel(i))) return true;
  }
  return false;
}

Try / catch

try {
  int v = rs.getInt(label);
} catch (SQLException e) {
  if (!String.valueOf(e.getMessage()).contains("not found")) throw e;
  // dump actual labels for a quick fix
  java.sql.ResultSetMetaData md = rs.getMetaData();
  for (int i = 1; i <= md.getColumnCount(); i++) log.error("column {}: {}", i, md.getColumnLabel(i));
  throw e;
}

Prevention

When it happens

Trigger: Case mismatch ('ID' vs 'id'); expressions without aliases (SELECT COUNT(*) must be aliased AS cnt to be addressable); using the base column name when the SELECT aliases it differently; typos; labels carrying leading/trailing whitespace from config files.

Common situations: Porting from drivers that fall back to case-insensitive matching; dynamic query builders where the SELECT list and the Java reader disagree; UNION queries whose branches alias differently; SQL copied with the alias dropped.

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/14ad9edbf1d1e072. Report an issue: GitHub.