apache/shardingsphere · error · IllegalArgumentException

Expected blr_begin

Error message

Expected blr_begin

What it means

SQLFeatureNotSupportedException thrown by updateBlob(int, InputStream) on SQL federation ResultSets. This is the stream-based overload of updateBlob among six, all declared final and throwing in AbstractUnsupportedUpdateOperationSQLFederationResultSet. Federation returns CONCUR_READ_ONLY in-memory merged results, so streaming bytes into a column of the current row is not possible; the throw occurs immediately, before the stream is read.

Source

Thrown at database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/batch/FirebirdParseBatchBlr.java:84

     * rejection happens later on the command/error path rather than as a codec-level channel close.</p>
     *
     * @param blr BLR buffer
     * @param blrLength BLR length
     * @return parsed message format
     * @throws IllegalArgumentException when BLR format is structurally invalid
     */
    public static FirebirdParseBatchBlr parseForFraming(final ByteBuf blr, final int blrLength) {
        if (blrLength < HEADER_LENGTH) {
            throw new IllegalArgumentException("BLR is too short: " + blrLength);
        }
        ByteBuf buffer = blr.duplicate();
        final int startReaderIndex = buffer.readerIndex();
        int version = buffer.readUnsignedByte();
        if (BlrConstants.blr_version4 != version && BlrConstants.blr_version5 != version) {
            throw new IllegalArgumentException("Unsupported BLR version: " + version);
        }
        if (BlrConstants.blr_begin != buffer.readUnsignedByte()) {
            throw new IllegalArgumentException("Expected blr_begin");
        }
        if (BlrConstants.blr_message != buffer.readUnsignedByte()) {
            throw new IllegalArgumentException("Expected blr_message");
        }
        buffer.skipBytes(1);
        FirebirdParseBatchBlr result = parseFormat(buffer);
        validateTermination(buffer, startReaderIndex, blrLength);
        return result;
    }
    
    private static void validateTermination(final ByteBuf buffer, final int startReaderIndex, final int blrLength) {
        if (remainingWithinBlr(buffer, startReaderIndex, blrLength) < 1 || BlrConstants.blr_end != buffer.readUnsignedByte()) {
            throw new IllegalArgumentException("Expected blr_end");
        }
        if (remainingWithinBlr(buffer, startReaderIndex, blrLength) < 1 || BlrConstants.blr_eoc != buffer.readUnsignedByte()) {
            throw new IllegalArgumentException("Expected blr_eoc");
        }
        if (0 != remainingWithinBlr(buffer, startReaderIndex, blrLength)) {

View on GitHub (pinned to e952770a21)

Solutions

  1. Use an UPDATE with ps.setBinaryStream(index, stream) so the data flows through normal DML
  2. Close/skip the stream properly in error paths since the federation call never consumes it
  3. Check rs.getConcurrency() upfront and choose DML for read-only cursors
  4. Sidestep federation for this statement if updatable behavior is required

Example fix

// before
rs.updateBlob(2, uploadInputStream); // SQLFeatureNotSupportedException
rs.updateRow();

// after
try (PreparedStatement ps = conn.prepareStatement("UPDATE files SET content = ? WHERE id = ?")) {
    ps.setBinaryStream(1, uploadInputStream);
    ps.setLong(2, rs.getLong("id"));
    ps.executeUpdate();
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (rs.getConcurrency() != ResultSet.CONCUR_UPDATABLE) {
    // stream upload via UPDATE + setBinaryStream instead of updateBlob(index, stream)
}

Try / catch

try {
    rs.updateBlob(columnIndex, inputStream);
} catch (SQLFeatureNotSupportedException e) {
    // caller must close/consume the stream; retry as UPDATE DML
}

Prevention

When it happens

Trigger: Calling rs.updateBlob(columnIndex, inputStream) on a federation ResultSet and then rs.updateRow(). Seen in upload pipelines that stream a file straight into the current row.

Common situations: Web upload handlers streaming multipart data into BLOB columns via the cursor API; code ported from drivers where CONCUR_UPDATABLE ResultSets accepted streams; ShardingSphere adopted as a proxy in front of an existing app without auditing cursor-update usage.

Related errors


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