apache/shardingsphere · error · IllegalStateException

Failed to read java.sql.Clob

Error message

Failed to read java.sql.Clob

What it means

Thrown while serializing a java.sql.Clob parameter for the Firebird wire protocol: reading the Clob (length(), getSubString()) raised SQLException. The proxy converts the Clob to bytes using the connection charset and registers it as a BLOB; any JDBC failure during that read is wrapped in IllegalStateException with the cause attached.

Source

Thrown at database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/FirebirdBlobBinaryProtocolValue.java:116

            blobId = (Long) value;
        } else if (value instanceof byte[]) {
            blobId = register(payload.getConnectionId(), (byte[]) value);
        } else if (value instanceof Blob) {
            try {
                blobId = register(payload.getConnectionId(), readAllBytes(((Blob) value).getBinaryStream()));
            } catch (final SQLException ex) {
                throw new IllegalStateException("Failed to read java.sql.Blob stream", ex);
            } catch (final IOException ex) {
                throw new IllegalStateException("Failed to read java.sql.Blob content", ex);
            }
        } else if (value instanceof Clob) {
            try {
                Clob clob = (Clob) value;
                int len = (int) Math.min(Integer.MAX_VALUE, clob.length());
                String str = clob.getSubString(1L, len);
                blobId = register(payload.getConnectionId(), str.getBytes(payload.getCharset()));
            } catch (final SQLException ex) {
                throw new IllegalStateException("Failed to read java.sql.Clob", ex);
            }
        } else {
            blobId = register(payload.getConnectionId(), value.toString().getBytes(payload.getCharset()));
        }
        
        payload.writeInt8(blobId);
    }
    
    @Override
    public int getLength(final FirebirdPacketPayload payload) {
        return 8;
    }
}

View on GitHub (pinned to e952770a21)

Solutions

  1. Read the chained SQLException to confirm the lifetime/validity problem
  2. Create, bind, and execute the Clob within one open connection and transaction
  3. Bind String or byte[] instead of java.sql.Clob when the content is in memory
  4. Verify the connection charset can encode the Clob content; encode explicitly with setBytes() if charset behavior is unclear

Example fix

// before: Clob used after its connection/transaction ended
ps.setClob(1, staleClob);

// after: bind the string directly on a live connection
ps.setString(1, text);
Defensive patterns

Strategy: validation

Validate before calling

// verify the Clob is servicable on this connection before binding
private static boolean isClobUsable(final Clob clob, final Connection conn) {
    try {
        return !conn.isClosed() && clob.length() >= 0;
    } catch (SQLException ex) {
        return false;
    }
}
// if (!isClobUsable(clob, conn)) ps.setString(1, reloadedText);

Try / catch

try {
    ps.setClob(1, clob);
} catch (IllegalStateException ex) {
    if (ex.getCause() instanceof SQLException) {
        ps.setString(1, readClobAgainFromSource()); // fall back to String binding
    } else {
        throw ex;
    }
}

Prevention

When it happens

Trigger: Binding a java.sql.Clob parameter where clob.length() or clob.getSubString(1, len) throws — the Clob's owning connection is closed, the Clob was freed, or the driver cannot serve it in the current transaction state.

Common situations: Clobs carried across connection boundaries; free() called before execute; drivers that invalidate temp Clobs on commit (similar lifetime issues as Blob, plus charset conversion via payload.getCharset()).

Related errors


AI-assisted analysis of apache/shardingsphere@e952770a21 (2026-08-14). Data as JSON: /api/errors/0f3f61aea0ae5d9a. Report an issue: GitHub.