{"record":{"id":"780928214f0b2b66","repo":"apache/shardingsphere","slug":"failed-to-read-java-sql-blob-stream","errorCode":null,"errorMessage":"Failed to read java.sql.Blob stream","messagePattern":"Failed to read java\\.sql\\.Blob stream","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/FirebirdBlobBinaryProtocolValue.java","lineNumber":105,"sourceCode":"    public Object read(final FirebirdPacketPayload payload) {\n        return payload.readInt8();\n    }\n    \n    @Override\n    public void write(final FirebirdPacketPayload payload, final Object value) {\n        long blobId;\n        \n        if (null == value) {\n            blobId = 0L;\n        } else if (value instanceof Long) {\n            blobId = (Long) value;\n        } else if (value instanceof byte[]) {\n            blobId = register(payload.getConnectionId(), (byte[]) value);\n        } else if (value instanceof Blob) {\n            try {\n                blobId = register(payload.getConnectionId(), readAllBytes(((Blob) value).getBinaryStream()));\n            } catch (final SQLException ex) {\n                throw new IllegalStateException(\"Failed to read java.sql.Blob stream\", ex);\n            } catch (final IOException ex) {\n                throw new IllegalStateException(\"Failed to read java.sql.Blob content\", ex);\n            }\n        } else if (value instanceof Clob) {\n            try {\n                Clob clob = (Clob) value;\n                int len = (int) Math.min(Integer.MAX_VALUE, clob.length());\n                String str = clob.getSubString(1L, len);\n                blobId = register(payload.getConnectionId(), str.getBytes(payload.getCharset()));\n            } catch (final SQLException ex) {\n                throw new IllegalStateException(\"Failed to read java.sql.Clob\", ex);\n            }\n        } else {\n            blobId = register(payload.getConnectionId(), value.toString().getBytes(payload.getCharset()));\n        }\n        \n        payload.writeInt8(blobId);\n    }","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/apache/shardingsphere/blob/e952770a215630a3659c75d64369168cd3e26b82/database/protocol/dialect/firebird/src/main/java/org/apache/shardingsphere/database/protocol/firebird/packet/command/query/statement/execute/protocol/FirebirdBlobBinaryProtocolValue.java#L87-L123","documentation":"Thrown while serializing a java.sql.Blob parameter for the Firebird wire protocol: the proxy opens the Blob's binary stream to read all bytes, and the JDBC call (getBinaryStream or the stream read) raised SQLException. The original exception is chained. It almost always means the Blob is backed by a closed connection or an already-freed temporary LOB.","triggerScenarios":"Setting a java.sql.Blob parameter on a Firebird statement through the proxy where blob.getBinaryStream() or the subsequent stream read throws SQLException — typically because the connection/transaction that created the Blob was closed or rolled back before execution.","commonSituations":"Reusing a Blob obtained in a previous (now-committed or closed) connection; holding a Lob past free()/close(); driver-level LOB lifetime restrictions (free_temporary on commit).","solutions":["Check the chained SQLException cause — 'connection closed', 'lob already freed', or similar pinpoints the lifetime problem","Create the Blob and execute the statement within the same open connection/transaction","Pass byte[] instead of java.sql.Blob when the data is already materialized in memory","Call connection.commit()/keep the transaction alive per your driver's LOB lifetime rules before using the Blob"],"exampleFix":"// before: Blob created on one connection, used after it was closed\ntry (Connection c1 = ds.getConnection()) {\n    Blob blob = c1.createBlob();\n    blob.setBytes(1, data);\n    saveForLater(blob);            // c1 closes -> blob dead\n}\ntry (Connection c2 = ds.getConnection()) {\n    ps.setBlob(1, loadFromEarlier());  // throws\n}\n\n// after: create and use the Blob on the same live connection, or send bytes\ntry (Connection c = ds.getConnection()) {\n    ps.setBytes(1, data);\n    ps.executeUpdate();\n}","handlingStrategy":"validation","validationCode":"// validate the Blob is still usable before binding\nprivate static boolean isBlobReadable(final Blob blob, final Connection conn) {\n    try {\n        return !conn.isClosed() && blob.length() >= 0;\n    } catch (SQLException ex) {\n        return false;\n    }\n}\n// if (!isBlobReadable(blob, conn)) -> re-materialize bytes or fail early with a clear message","typeGuard":null,"tryCatchPattern":"try {\n    ps.setBlob(1, blob);\n} catch (IllegalStateException ex) {\n    Throwable cause = ex.getCause();\n    if (cause instanceof SQLException) {\n        // Lob lifetime issue: re-read source data and bind bytes instead\n        ps.setBytes(1, reloadFromSource());\n    } else {\n        throw ex;\n    }\n}","preventionTips":["Keep Blob creation, binding, and execution on the same open connection and transaction","Prefer byte[]/setBytes for values you already hold in memory","Never call Blob.free() before statement execution"],"tags":["firebird","jdbc","blob","lob-lifetime","serialization"],"backgroundTag":null,"analyzedSha":"e952770a215630a3659c75d64369168cd3e26b82","analyzedAt":"2026-08-14T13:54:53.392Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}