tursodatabase/turso · error · SQLException

Unsupported object type in bindObject: " + x.getClass().getN

Error message

Unsupported object type in bindObject: " + x.getClass().getName()

What it means

TursoStatement.bindObject dispatches on the runtime type of the argument and only accepts null, Integer, Long, String, Float, Double, and byte[]. Any other non-null type falls into the else branch and throws this SQLException naming the offending class. There is no implicit conversion for Boolean, Short, Byte, BigDecimal, java.util.Date, java.sql.Timestamp, java.time types, UUID, or enums.

Source

Thrown at bindings/java/src/main/java/tech/turso/core/TursoStatement.java:240

    }
    if (x instanceof Byte) {
      this.bindInt(parameterIndex, (Byte) x);
    } else if (x instanceof Short) {
      this.bindInt(parameterIndex, (Short) x);
    } else if (x instanceof Integer) {
      this.bindInt(parameterIndex, (Integer) x);
    } else if (x instanceof Long) {
      this.bindLong(parameterIndex, (Long) x);
    } else if (x instanceof String) {
      bindText(parameterIndex, (String) x);
    } else if (x instanceof Float) {
      bindDouble(parameterIndex, (Float) x);
    } else if (x instanceof Double) {
      bindDouble(parameterIndex, (Double) x);
    } else if (x instanceof byte[]) {
      bindBlob(parameterIndex, (byte[]) x);
    } else {
      throw new SQLException("Unsupported object type in bindObject: " + x.getClass().getName());
    }
  }

  /**
   * Returns total number of changes.
   *
   * @throws SQLException If a database access error occurs
   */
  public long totalChanges() throws SQLException {
    final long result = totalChanges(statementPointer);
    if (result == -1) {
      throw new SQLException("Exception while retrieving total number of changes");
    }

    return result;
  }

  private native long totalChanges(long statementPointer) throws SQLException;

View on GitHub (pinned to bad083fafb)

Solutions

  1. Convert the value to a supported type before binding: boolean -> int 0/1, Short/Byte -> Integer, BigDecimal -> longValue()/doubleValue(), Date/Timestamp/java.time -> ISO-8601 String, UUID -> String, enum -> name().
  2. Prefer the typed setters (setInt, setLong, setString, setDouble, setBytes) instead of setObject when the type is known.
  3. Centralize the mapping in one helper (toSupportedSqlValue) so unsupported types fail with a clear message at the boundary.
  4. Add a type guard that rejects unsupported classes before they reach bindObject.

Example fix

// before
ps.setObject(1, Boolean.TRUE);            // throws: java.lang.Boolean
ps.setObject(2, OffsetDateTime.now());    // throws: java.time.OffsetDateTime
ps.setObject(3, BigDecimal.valueOf("9.99")); // throws: java.math.BigDecimal

// after
ps.setInt(1, active ? 1 : 0);
ps.setString(2, OffsetDateTime.now().toString());
ps.setDouble(3, price.doubleValue());
Defensive patterns

Strategy: type-guard

Validate before calling

private static Object toSupportedSqlValue(Object x) {
    if (x == null) return null;
    if (x instanceof Boolean b) return b ? 1 : 0;
    if (x instanceof Short || x instanceof Byte) return ((Number) x).intValue();
    if (x instanceof BigDecimal bd) return bd.scale() <= 0 ? bd.longValue() : (Object) bd.doubleValue();
    if (x instanceof java.util.Date || x instanceof java.time.temporal.Temporal)
        return x instanceof java.sql.Timestamp t ? t : x.toString();
    if (x instanceof UUID) return x.toString();
    if (x instanceof Enum<?> e) return e.name();
    return x;
}

Type guard

private static boolean isSupportedBindValue(Object x) {
    return x == null
        || x instanceof Integer || x instanceof Long
        || x instanceof String  || x instanceof Float
        || x instanceof Double || x instanceof byte[];
}

Try / catch

try {
    stmt.bindObject(parameterIndex, x);
} catch (SQLException e) {
    throw new IllegalArgumentException(
        "unsupported bind value of type " + (x == null ? "null" : x.getClass().getName()), e);
}

Prevention

When it happens

Trigger: ps.setObject(i, Boolean.TRUE); ps.setObject(i, new Timestamp(...)); ps.setObject(i, BigDecimal.TEN); ps.setObject(i, someEnum); ps.setObject(i, UUID.randomUUID()); any generic DAO that forwards arbitrary objects to setObject.

Common situations: Generic ORM-lite layers and JDBC helpers that always call setObject; porting code from drivers with wider setObject type maps (PostgreSQL/MySQL accept many more types); domain objects with enums or money types bound directly.

Related errors


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