{"record":{"id":"ad7375b7ebfc2262","repo":"alibaba/druid","slug":"connection-holder-is-null","errorCode":null,"errorMessage":"connection holder is null","messagePattern":"connection holder is null","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java","lineNumber":1192,"sourceCode":"        if (closed) {\n            if (disableError != null) {\n                throw new SQLException(\"connection closed\", disableError);\n            } else {\n                throw new SQLException(\"connection closed\");\n            }\n        }\n\n        if (disable) {\n            if (disableError != null) {\n                throw new SQLException(\"connection disabled\", disableError);\n            } else {\n                throw new SQLException(\"connection disabled\");\n            }\n        }\n\n        if (holder == null) {\n            if (disableError != null) {\n                throw new SQLException(\"connection holder is null\", disableError);\n            } else {\n                throw new SQLException(\"connection holder is null\");\n            }\n        }\n    }\n\n    public String toString() {\n        if (conn != null) {\n            return conn.toString();\n        } else {\n            return \"closed-conn-\" + System.identityHashCode(this);\n        }\n    }\n\n    public void setSchema(String schema) throws SQLException {\n        if (JdbcUtils.isMysqlDbType(holder.dataSource.getDbType())) {\n            if (holder.initSchema == null) {\n                holder.initSchema = conn.getSchema();","sourceCodeStart":1174,"sourceCodeEnd":1210,"githubUrl":"https://github.com/alibaba/druid/blob/fa8dc9912637a2f729eef9f55356621fec18d40e/core/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java#L1174-L1210","documentation":"Thrown by DruidPooledConnection.checkStateInternal() when the connection is neither closed nor disabled but its holder is null — meaning the DruidConnectionHolder backing this pooled connection has been detached. If a disableError exists it is chained; otherwise thrown bare. This is an inconsistent state: the pooled wrapper exists but has no underlying physical connection handle.","triggerScenarios":"JDBC operation on a DruidPooledConnection where holder == null (closed and disable both false). Line 1192 (with cause) or line 1194 (without) throws. Occurs when the holder was cleared (e.g. after the physical connection was reclaimed) but the wrapper was not marked closed/disabled, or when a Filter/proxy detached the holder.","commonSituations":"Concurrent close racing with use such that holder is nulled before closed is set; a Filter or application code calling setHolder(null)-equivalent paths; using a connection after the pool reclaimed the underlying physical connection; a bug in custom connection wrapping.","solutions":["Avoid sharing a Connection across threads; the holder-null race is typically a concurrency artefact.","Scope every Connection in try-with-resources so it cannot outlive its holder.","If this appears with a chained disableError, treat it like error 57 — the underlying cause explains the reclamation.","Upgrade Druid if holder-null-without-cause appears: it usually indicates a lifecycle race fixed in newer versions."],"exampleFix":"// before — shared connection, holder nulled by another thread\nprivate Connection shared;\nvoid t1() { shared = dataSource.getConnection(); }\nvoid t2() { shared.createStatement(); } // holder null -> SQLException\n\n// after — per-thread borrow\nvoid run() {\n    try (Connection c = dataSource.getConnection(); Statement s = c.createStatement()) {\n        s.execute(SQL);\n    }\n}","handlingStrategy":"validation","validationCode":"if (pooledConn.getHolder() == null) {\n    Throwable why = pooledConn.getDisableError();\n    throw new IllegalStateException(\"holder detached\" + (why == null ? \"\" : \": \" + why));\n}\n// proceed","typeGuard":"boolean usable = pooledConn != null && pooledConn.getHolder() != null\n    && !pooledConn.isClosed() && !pooledConn.isDisable();","tryCatchPattern":"try {\n    pooledConn.createStatement();\n} catch (SQLException e) {\n    if (\"connection holder is null\".equals(e.getMessage())) {\n        // inconsistent state; abandon this wrapper and borrow fresh\n        log.error(\"pooled conn holder detached; state race?\", e.getCause());\n        borrowFreshAndRetry();\n    } else throw e;\n}","preventionTips":["Never share a Connection across threads — holder-null is usually a concurrency race.","Scope every Connection in try-with-resources.","Upgrade Druid if holder-null-without-cause appears in single-threaded code."],"tags":["holder-null","concurrency","lifecycle","connection-pool","race"],"backgroundTag":null,"analyzedSha":"fa8dc9912637a2f729eef9f55356621fec18d40e","analyzedAt":"2026-08-14T04:55:06.789Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}