{"record":{"id":"4da1530d599ae33c","repo":"alibaba/druid","slug":"connection-closed","errorCode":null,"errorMessage":"connection closed","messagePattern":"connection closed","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java","lineNumber":1176,"sourceCode":"            asyncCloseEnabled = false;\n        }\n\n        if (asyncCloseEnabled) {\n            lock.lock();\n            try {\n                checkStateInternal();\n            } finally {\n                lock.unlock();\n            }\n        } else {\n            checkStateInternal();\n        }\n    }\n\n    private void checkStateInternal() throws SQLException {\n        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\");","sourceCodeStart":1158,"sourceCodeEnd":1194,"githubUrl":"https://github.com/alibaba/druid/blob/fa8dc9912637a2f729eef9f55356621fec18d40e/core/src/main/java/com/alibaba/druid/pool/DruidPooledConnection.java#L1158-L1194","documentation":"Thrown by DruidPooledConnection.checkStateInternal() when the connection is closed AND a disableError is set. The disableError (the exception that caused Druid to disable the connection) is chained as cause, giving you the original failure (e.g. a fatal DB error) alongside the 'closed' status.","triggerScenarios":"Any JDBC operation on a DruidPooledConnection whose closed flag is true and disableError is non-null triggers checkState() -> checkStateInternal(), throwing at line 1176. Typically the connection was closed by Druid's async-close / keep-alive / fatal-error handling after a detected failure, and the application still holds a stale reference.","commonSituations":"Using a connection after handleFatalError disabled and closed it (exceptionSorter flagged a fatal error); keepAlive/eviction closed an idle connection the app still references; async-close (asyncCloseConnectionEnable) racing with a slow statement; a connection returned to a caller after the pool reclaimed it; leaked connection reused across requests.","solutions":["Always use try-with-resources for Connection/Statement/ResultSet so a closed connection is never used again.","Read the chained disableError to find why Druid closed it (fatal SQL error, validation failure) and fix the upstream cause.","Avoid holding a Connection across method/RPC boundaries where async-close or eviction can reclaim it.","If asyncCloseConnectionEnable is on and causing races, review whether you need it; ensure statements complete before returning the connection."],"exampleFix":"// before — connection reused after a fatal error closed it\nConnection c = pool.borrow();\nrunQuery(c); // fatal error -> c closed\nrunQuery(c); // SQLException(\"connection closed\", disableError)\n\n// after — fresh connection each unit of work\ntry (Connection c = pool.borrow()) {\n    runQuery(c);\n}","handlingStrategy":"validation","validationCode":"if (pooledConn.isClosed()) {\n    Throwable why = pooledConn.getDisableError();\n    throw new IllegalStateException(\"connection already closed\" + (why == null ? \"\" : \": \" + why));\n}\n// proceed to use the connection","typeGuard":"boolean usable = pooledConn != null && !pooledConn.isClosed()\n    && !pooledConn.isDisable() && pooledConn.getHolder() != null;","tryCatchPattern":"try {\n    pooledConn.createStatement();\n} catch (SQLException e) {\n    if (\"connection closed\".equals(e.getMessage()) && e.getCause() != null) {\n        // disableError explains why Druid closed it; borrow fresh\n        log.error(\"conn closed by Druid due to: {}\", e.getCause());\n        borrowFreshAndRetry();\n    } else throw e;\n}","preventionTips":["Always scope Connection in try-with-resources so it is never used after close.","Read disableError to learn why Druid closed the connection and fix the root cause.","Do not hold connections across async-close or eviction windows."],"tags":["connection-closed","fatal-error","lifecycle","connection-pool","chained-cause"],"backgroundTag":null,"analyzedSha":"fa8dc9912637a2f729eef9f55356621fec18d40e","analyzedAt":"2026-08-14T04:55:06.789Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}