tursodatabase/turso · error · java.sql.SQLException

Unsupported object type in bindObject: {className}

Error message

Unsupported object type in bindObject: {className}

What it means

bindObject dispatches on exact runtime types and only accepts null, Byte, Short, Integer, Long, String, Float, Double, and byte[]; every other class falls through to this throw with the class name appended. There is no widening or coercion, so Boolean, Character, BigDecimal, java.util.Date, java.time types, enums, and UUID all fail even though they feel 'bindable'. This is the backing of setObject-style JDBC APIs.

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 244cde92a7)

Solutions

  1. Convert before binding: boolean -> 0/1 Integer, BigDecimal -> long/double or text, temporal -> ISO-8601 String or epoch Long, Character -> String, UUID -> String
  2. Add your own dispatch helper that maps your domain types onto the supported set
  3. For untyped data, normalize values at the boundary (right after deserialization) rather than at bind time

Example fix

// before
stmt.bindObject(1, activeFlag);          // Boolean -> throws
stmt.bindObject(2, amount);              // BigDecimal -> throws

// after
stmt.bindObject(1, activeFlag ? 1 : 0);              // boolean as Integer
stmt.bindObject(2, amount.longValueExact());         // or toPlainString() for text
Defensive patterns

Strategy: type-guard

Validate before calling

Object x = value;
if (!isSupportedBindValue(x)) {
  x = convertForBind(x); // your mapping for Boolean, BigDecimal, temporal, UUID, ...
}
stmt.bindObject(1, x);

Type guard

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

Try / catch

try {
  stmt.bindObject(i, x);
} catch (SQLException e) {
  throw new IllegalArgumentException(
      "unsupported bind type " + x.getClass().getName(), e);
}

Prevention

When it happens

Trigger: Calling stmt.bindObject(i, x) / setObject with Boolean.TRUE, a BigDecimal, a LocalDate or java.util.Date, a Character, a UUID, or any domain object; receiving untyped values (Map<String,Object> from JSON deserialization) and forwarding them directly.

Common situations: Generic persistence layers that pass through whatever the JSON deserializer produced; DTOs with BigDecimal amounts or boolean flags; ports of code from drivers that auto-convert more types.

Related errors


AI-assisted analysis of tursodatabase/turso@244cde92a7 (2026-08-20). Data as JSON: /api/errors/e171a64085ab77e6. Report an issue: GitHub.