tursodatabase/turso · error · SQLException

Cannot convert value to Date: {type}

Error message

Cannot convert value to Date: {type}

What it means

getDate(int) accepts only this driver's own date representation: a byte[] of exactly 8 bytes holding epoch milliseconds — exactly what setDate writes (Long.BYTES buffer, putLong). If the column holds TEXT ('2024-01-31') or an INTEGER epoch value, the value arrives as String/Long and the conversion branch throws, naming the actual class. With SQLite's dynamic typing, the write path and read path must agree on the representation.

Source

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

  @Override
  @Nullable
  public Date getDate(int columnIndex) throws SQLException {
    final Object result = resultSet.get(columnIndex);
    wasNull = result == null;
    if (result == null) {
      return null;
    }
    return wrapTypeConversion(
        () -> {
          if (result instanceof byte[]) {
            byte[] bytes = (byte[]) result;
            if (bytes.length == Long.BYTES) {
              long time = ByteBuffer.wrap(bytes).getLong();
              return new Date(time);
            }
          }
          throw new SQLException("Cannot convert value to Date: " + result.getClass());
        });
  }

  @Override
  @SkipNullableCheck
  public Time getTime(int columnIndex) throws SQLException {
    final Object result = resultSet.get(columnIndex);
    wasNull = result == null;
    if (result == null) {
      return null;
    }
    return wrapTypeConversion(
        () -> {
          if (result instanceof byte[]) {
            byte[] bytes = (byte[]) result;
            if (bytes.length == Long.BYTES) {
              long time = ByteBuffer.wrap(bytes).getLong();
              return new Time(time);

View on GitHub (pinned to bad083fafb)

Solutions

  1. Read the raw value and parse in Java: java.sql.Date.valueOf(rs.getString(i)) for 'yyyy-[m]m-[d]d' text, or new Date(rs.getLong(i)) for epoch millis.
  2. Write dates with setDate()/setTimestamp() so the 8-byte blob round-trips through getDate().
  3. Normalize at the SQL level: select the column as text and parse, or migrate the column once so all rows share one representation.

Example fix

// before
java.sql.Date d = rs.getDate(1); // TEXT column '2024-01-31' -> throws

// after
java.sql.Date d = java.sql.Date.valueOf(rs.getString(1));
Defensive patterns

Strategy: type-guard

Validate before calling

Object v = rs.getObject(1);
java.sql.Date d = (v == null) ? null
    : isDriverDateBlob(v) ? rs.getDate(1)
    : java.sql.Date.valueOf(rs.getString(1)); // TEXT fallback

Type guard

static boolean isDriverDateBlob(Object v) {
    return v instanceof byte[] b && b.length == Long.BYTES;
}

Try / catch

try {
    return rs.getDate(i);
} catch (SQLException e) {
    if (e.getMessage() != null && e.getMessage().contains("Cannot convert value to Date")) {
        return java.sql.Date.valueOf(rs.getString(i)); // TEXT-column fallback
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling rs.getDate() on a TEXT date column filled with string literals or by other tools; on an INTEGER epoch-seconds column; on rows written by a different driver that stores dates as text.

Common situations: Tables shared with the sqlite3 CLI or other ORMs that write ISO-8601 text; mixed writers on one database; assuming the string-coercion behavior of other JDBC drivers.

Related errors


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