{"record":{"id":"52ed934c421cbac5","repo":"hibernate/hibernate-orm","slug":"unable-to-access-nclob-stream","errorCode":null,"errorMessage":"Unable to access nclob stream","messagePattern":"Unable to access nclob stream","errorType":"exception","errorClass":"HibernateException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/type/descriptor/java/NClobJavaType.java","lineNumber":150,"sourceCode":"\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// otherwise we need to build a Reader...\n\t\t\t\t\treturn type.cast( value.getCharacterStream() );\n\t\t\t\t}\n\t\t\t}\n\t\t\telse if ( CharacterStream.class.isAssignableFrom( type ) ) {\n\t\t\t\tif (value instanceof NClobImplementer clobImplementer) {\n\t\t\t\t\t// if the incoming NClob is a wrapper, just pass along its CharacterStream\n\t\t\t\t\treturn type.cast( clobImplementer.getUnderlyingStream() );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// otherwise we need to build a CharacterStream...\n\t\t\t\t\treturn type.cast( new CharacterStreamImpl( value.getCharacterStream(), value.length() ) );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcatch ( SQLException e ) {\n\t\t\tthrow new HibernateException( \"Unable to access nclob stream\", e );\n\t\t}\n\n\t\tthrow unknownUnwrap( type );\n\t}\n\n\tpublic <X> NClob wrap(X value, WrapperOptions options) {\n\t\tif ( value == null ) {\n\t\t\treturn null;\n\t\t}\n\t\telse {\n\t\t\tfinal LobCreator lobCreator = options.getLobCreator();\n\t\t\tif ( value instanceof NClob clob ) {\n\t\t\t\treturn lobCreator.wrap( clob );\n\t\t\t}\n\t\t\telse if ( value instanceof Clob clob ) {\n\t\t\t\ttry {\n\t\t\t\t\treturn lobCreator.createNClob( clob.getCharacterStream(), clob.length() );\n\t\t\t\t}","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/NClobJavaType.java#L132-L168","documentation":"In NClobJavaType.unwrap(), converting an NClob to a Reader/CharacterStream/String calls getCharacterStream()/length() on the live LOB. Any SQLException from the driver is wrapped as HibernateException(\"Unable to access nclob stream\"). The usual root cause is an invalidated LOB locator: the stream was already consumed, free() was called, or the owning transaction/connection has closed.","triggerScenarios":"Accessing a lazy NClob attribute after the session was closed or the transaction committed; reading the character stream twice from a forward-only locator; Oracle/DB2-style drivers that free temporary LOBs at commit while the entity is still referenced","commonSituations":"Detached entities whose NClob fields are read in the view layer after the transaction ended; long request processing holding entities across commits; async processing or thread handoff after the session-bound work finished","solutions":["Fully read/materialize the NClob while the session and transaction are open (e.g. in @Transactional service code)","Map the column as materialized String (@Lob @Nationalized String) or byte[] so no live locator is retained","Keep the session open until LOB content is consumed, or re-attach and refresh within a new transaction before reading","For drivers that free temp LOBs at commit, copy the LOB content before committing (stream it into a String)"],"exampleFix":"// before\n@Transactional(readOnly = true)\npublic Article load(Long id) { return repo.findById(id).orElseThrow(); }\n// caller later reads article.getBody().getCharacterStream() -> HibernateException\n\n// after\n@Transactional(readOnly = true)\npublic String loadBody(Long id) {\n    return repo.findById(id).map(a -> a.getBodyText()).orElseThrow(); // materialized String\n}\n// entity uses: @Nationalized @Lob String bodyText;","handlingStrategy":"try-catch","validationCode":"static String readWithinTx(jakarta.persistence.EntityManager em, Long id) {\n    return em.executeInTransaction(() -> {\n        var a = em.find(Article.class, id);\n        return a == null ? null : readFully(a.getBody()); // consume NClob now\n    });\n}\n\nstatic String readFully(java.sql.NClob clob) throws java.sql.SQLException {\n    try (var r = clob.getCharacterStream()) { return r.lines().collect(java.util.stream.Collectors.joining(\"\\n\")); }\n}","typeGuard":"static boolean isUsable(java.sql.NClob c) {\n    try { c.length(); return true; } catch (java.sql.SQLException e) { return false; }\n}","tryCatchPattern":"try {\n    return readFully(article.getBody());\n} catch (HibernateException e) {\n    if (\"Unable to access nclob stream\".equals(e.getMessage()) && e.getCause() instanceof java.sql.SQLException) {\n        // locator invalid: re-read inside a fresh transaction instead of retrying the dead handle\n        return retryInNewTransaction(article.getId());\n    }\n    throw e;\n}","preventionTips":["Consume LOB streams inside the owning transaction; never in the view layer","Map LOBs as materialized String when the content size is manageable","Copy temp LOB content to a String before commit on drivers that free locators at commit"],"tags":["hibernate","nclob","lob-stream","detached-entity","transaction-scope","oracle"],"backgroundTag":"lob-stream-access-failure","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}