{"record":{"id":"f34a4075df8943d5","repo":"hibernate/hibernate-orm","slug":"cannot-redefine-the-tenant-identifier-on-a-child-s","errorCode":null,"errorMessage":"Cannot redefine the tenant identifier on a child session if the connection is reused","messagePattern":"Cannot redefine the tenant identifier on a child session if the connection is reused","errorType":"exception","errorClass":"SessionException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedSessionBuilderImpl.java","lineNumber":68,"sourceCode":"\n\t// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\t// SharedSessionBuilder\n\n\t@Override\n\t@Nonnull\n\tpublic SharedSessionBuilderImplementor withOption(EntityManager.CreationOption option) {\n\t\toptions.apply( option );\n\t\treturn this;\n\t}\n\n\t@Override\n\t@Nonnull\n\tpublic SessionImplementor open() {\n\t\tCORE_LOGGER.openingSession( options.getTenantIdentifierValue() );\n\t\tif ( original.getFactory().getSessionFactoryOptions().isMultiTenancyEnabled() ) {\n\t\t\tif ( options.isTransactionCoordinatorShared() ) {\n\t\t\t\tif ( tenantIdChanged ) {\n\t\t\t\t\tthrow new SessionException(\n\t\t\t\t\t\t\t\"Cannot redefine the tenant identifier on a child session if the connection is reused\" );\n\t\t\t\t}\n\t\t\t\tif ( readOnlyChanged ) {\n\t\t\t\t\tthrow new SessionException(\n\t\t\t\t\t\t\t\"Cannot redefine the read-only mode on a child session if the connection is reused\" );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn createSession( options );\n\t}\n\n\t@Override\n\t@Nonnull\n\tpublic SessionImplementor openSession() {\n\t\treturn open();\n\t}\n\n\t@Override","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/engine/creation/internal/SharedSessionBuilderImpl.java#L50-L86","documentation":"When you derive a child session from an existing one via session.sessionWithOptions(), the builder can share the parent's transaction coordinator (and therefore its physical connection). If multi-tenancy is enabled and the tenant identifier was changed on the builder (tenantIdentifier with a different value sets tenantIdChanged), Hibernate refuses to open the child session: the shared connection is already bound to the original tenant, so a different tenant on the same connection would break tenant isolation.","triggerScenarios":"session.sessionWithOptions()...openSession() while sharing the transaction (the default when reusing the connection/transaction), with multi-tenancy enabled (MultiTenantConnectionProvider configured), and .tenantIdentifier(...) called with a value different from the parent session's tenant.","commonSituations":"Background jobs that open a worker session from a request session but switch tenants; incorrect assumption that sessionWithOptions() creates an independent connection; DATABASE/SHEMA/SCHEMA multi-tenant setups where the tenant decides which connection/schema is used.","solutions":["Do not change the tenant on a child session that shares the parent's connection - drop the .tenantIdentifier(...) call if the same tenant is intended.","Open a completely separate session (factory.openSession(tenantId)) for the other tenant so it gets its own connection from the tenant connection provider.","Stop sharing the transaction coordinator on the builder (do not call .connection()/.transaction() sharing options) if you truly need a different tenant in the same thread.","Review the design: cross-tenant work on a shared connection is exactly what this guard prevents."],"exampleFix":"// before - shares parent connection but switches tenant -> SessionException\ntry (Session child = requestSession.sessionWithOptions()\n        .connection()                      // reuse parent connection\n        .tenantIdentifier(\"tenant-b\")      // different tenant\n        .openSession()) { ... }\n// after - independent session gets its own connection for the tenant\ntry (Session other = sessionFactory.openSession(tenantBId)) { ... }","handlingStrategy":"validation","validationCode":"// Before opening a shared child session, assert the tenant is unchanged\nif (factory.getSessionFactoryOptions().isMultiTenancyEnabled()\n        && !Objects.equals(desiredTenant, parentSession.getTenantIdentifierValue())) {\n    // must NOT share the parent's connection/transaction with another tenant\n    return factory.openSession(desiredTenant);\n}\nreturn parentSession.sessionWithOptions().connection().openSession();","typeGuard":null,"tryCatchPattern":"catch (SessionException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"tenant identifier on a child session\")) {\n        // recover by opening an independent session for the target tenant\n        return factory.openSession(desiredTenant);\n    }\n    throw e;\n}","preventionTips":["Treat sessionWithOptions().connection() as 'same tenant, same mode only'.","Encapsulate child-session creation in one helper that compares tenant ids first.","Give each tenant its own session from the factory rather than reusing a parent connection."],"tags":["multi-tenancy","session","shared-connection","tenant-isolation","session-builder"],"backgroundTag":"multi-tenant-session-misuse","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}