{"record":{"id":"46b847938af8d38d","repo":"hibernate/hibernate-orm","slug":"unable-to-access-lob-stream-46b847","errorCode":null,"errorMessage":"Unable to access lob stream","messagePattern":"Unable to access lob stream","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java","lineNumber":159,"sourceCode":"\t\tif ( wrapped == null ) {\n\t\t\tthrow unknownWrap( value.getClass() );\n\t\t}\n\t\treturn wrapped;\n\t}\n\n\tprivate <X> @Nullable byte[] wrapOrNull(@Nonnull X value) {\n\t\tif (value instanceof byte[] bytes) {\n\t\t\treturn bytes;\n\t\t}\n\t\tif (value instanceof InputStream inputStream) {\n\t\t\treturn DataHelper.extractBytes( inputStream );\n\t\t}\n\t\tif ( value instanceof Blob blob ) {\n\t\t\ttry {\n\t\t\t\treturn DataHelper.extractBytes( blob.getBinaryStream() );\n\t\t\t}\n\t\t\tcatch ( SQLException e ) {\n\t\t\t\tthrow new HibernateException( \"Unable to access lob stream\", e );\n\t\t\t}\n\t\t}\n\t\telse if ( value instanceof Byte byteValue ) {\n\t\t\t// Support binding a single element as parameter value\n\t\t\treturn new byte[]{ byteValue };\n\t\t}\n\t\telse if ( value instanceof Byte[] array ) {\n\t\t\tfinal byte[] bytes = new byte[array.length];\n\t\t\tfor ( int i = 0; i < array.length; i++ ) {\n\t\t\t\tbytes[i] = array[i];\n\t\t\t}\n\t\t\treturn bytes;\n\t\t}\n\t\treturn null;\n\t}\n\n\t@Override\n\tpublic @Nullable byte[] coerce(@Nullable Object value) {","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/PrimitiveByteArrayJavaType.java#L141-L177","documentation":"PrimitiveByteArrayJavaType.wrapOrNull() converts an incoming java.sql.Blob into byte[] by calling getBinaryStream() and fully reading it; any SQLException from the driver (invalidated locator, stream already consumed, connection closed, LOB freed) is wrapped as HibernateException(\"Unable to access lob stream\"). Like the NClob variant, this almost always means the Blob handle outlived the transaction or connection that owned it.","triggerScenarios":"A byte[] attribute mapped from a BLOB column is lazily materialized after the session/transaction closed; the Blob stream is read twice or partially consumed elsewhere; drivers (Oracle, DB2) that auto-release temporary LOBs at commit","commonSituations":"Lazy-loaded attachments read in a view layer after the transaction ended; entities serialized/deserialized across requests with live Blob handles; background jobs processing entities after their session ended","solutions":["Materialize byte[] content while the session and transaction are open (eager fetch or explicit read in service code)","Keep the session open until the content is read, or refresh the entity inside a new transaction before accessing the field","Avoid holding live java.sql.Blob handles across requests - copy to byte[] immediately after load","Check driver settings/docs regarding temporary LOB lifetime at commit (Oracle temp LOBs)"],"exampleFix":"// before\nAttachment a = repo.findById(id).get(); // session closes after tx\nbyte[] data = a.getData(); // lazy Blob wrap after close -> HibernateException\n\n// after\n@Transactional(readOnly = true)\nbyte[] loadNow(Long id) {\n    return repo.findById(id).map(Attachment::getData).orElseThrow(); // read inside tx\n}\n// or map the column directly as byte[] with eager materialization","handlingStrategy":"try-catch","validationCode":"static byte[] materializeInTx(jakarta.persistence.EntityManager em, Long id) {\n    return em.executeInTransaction(() -> {\n        Attachment a = em.find(Attachment.class, id);\n        return a == null ? null : a.getData(); // Blob -> byte[] while locator is live\n    });\n}","typeGuard":"static boolean isUsable(java.sql.Blob b) {\n    try { b.length(); return true; } catch (java.sql.SQLException e) { return false; }\n}","tryCatchPattern":"try {\n    return attachment.getData();\n} catch (HibernateException e) {\n    if (\"Unable to access lob stream\".equals(e.getMessage()) && e.getCause() instanceof java.sql.SQLException) {\n        return reloadInsideTransaction(attachment.getId()); // fresh locator, not a blind retry\n    }\n    throw e;\n}","preventionTips":["Materialize byte[] from Blob inside the transaction that loaded the entity","Do not detach/serialize entities holding live Blob handles","Read LOB streams exactly once; copy content if multiple consumers need it"],"tags":["hibernate","blob","byte-array","lob-stream","transaction-scope","lazy-loading"],"backgroundTag":"lob-stream-access-failure","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}