{"record":{"id":"5c6bbf5f239ae552","repo":"mybatis/mybatis-3","slug":"error-accessing-pooledconnection-connection-is-in","errorCode":null,"errorMessage":"Error accessing PooledConnection. Connection is invalid.","messagePattern":"Error accessing PooledConnection\\. Connection is invalid\\.","errorType":"exception","errorClass":"SQLException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/datasource/pooled/PooledConnection.java","lineNumber":267,"sourceCode":"      dataSource.pushConnection(this);\n      return null;\n    }\n    try {\n      if (!Object.class.equals(method.getDeclaringClass())) {\n        // issue #579 toString() should never fail\n        // throw an SQLException instead of a Runtime\n        checkConnection();\n      }\n      return method.invoke(realConnection, args);\n    } catch (Throwable t) {\n      throw ExceptionUtil.unwrapThrowable(t);\n    }\n\n  }\n\n  private void checkConnection() throws SQLException {\n    if (!valid) {\n      throw new SQLException(\"Error accessing PooledConnection. Connection is invalid.\");\n    }\n  }\n\n}\n","sourceCodeStart":249,"sourceCodeEnd":272,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/datasource/pooled/PooledConnection.java#L249-L272","documentation":"PooledConnection is a dynamic proxy around a real JDBC Connection managed by PooledDataSource. When the pool invalidates a connection (it was closed back to the pool, detected as broken, or the pool was force-closed), the 'valid' flag is set false. Any subsequent method call on the proxy goes through checkConnection() and throws SQLException 'Error accessing PooledConnection. Connection is invalid.' instead of silently hitting a dead real connection.","triggerScenarios":"Holding a Connection obtained from PooledDataSource after returning it to the pool (e.g., via close()) and then calling createStatement()/prepareStatement() on the stale reference; using a connection after PooledDataSource.forceCloseAll(); keeping connections past their pool lifetime; two parts of the code sharing one proxied Connection where one closes it.","commonSituations":"Custom connection management code that caches java.sql.Connection references instead of asking the pool each time; wrappers that call close() then continue using the connection; shutdown hooks or tests that force-close the pool while workers still use connections; Spring-managed code mixing pooled and raw connections.","solutions":["Never cache or reuse a Connection after close() — get a fresh one from dataSource.getConnection() for each unit of work","Audit code paths where the connection reference escapes (passed to helpers, stored in fields/ThreadLocal) and scope them to a try-with-resources block","If forceCloseAll is being triggered (e.g., by finalize or a lifecycle hook), fix the shutdown ordering so workers finish before the pool closes","Catch SQLException on connection use and re-acquire a connection if the message indicates invalidity (defensive, only for connection-survival logic)"],"exampleFix":"// before\nConnection c = pooledDataSource.getConnection();\n// ... later, after c.close() returned it to the pool\nc.createStatement().execute(q); // SQLException: invalid\n\n// after\ntry (Connection c = pooledDataSource.getConnection();\n     Statement st = c.createStatement()) {\n  st.execute(q);\n}","handlingStrategy":"validation","validationCode":"// MyBatis PooledConnection exposes no public isValid of its own;\n// validate by checking your own usage discipline instead:\n// never use a Connection reference after close().\n// With the pool, you may probe cheaply:\ntry (Connection c = pooledDataSource.getConnection()) {\n  if (c.isValid(1)) { /* proceed */ }\n}","typeGuard":null,"tryCatchPattern":"try {\n  stmt = conn.createStatement();\n} catch (SQLException e) {\n  if (\"Error accessing PooledConnection. Connection is invalid.\".equals(e.getMessage())) {\n    conn = pooledDataSource.getConnection(); // re-acquire and retry once\n  } else throw e;\n}","preventionTips":["Scope every Connection to try-with-resources; no fields, no ThreadLocals, no escaping references","Do not share one connection across components where one may close it","Close workers before closing the pool at shutdown"],"tags":["mybatis","connection-pool","jdbc","lifecycle","stale-reference"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}