{"record":{"id":"eb6fc57de6305fad","repo":"mybatis/mybatis-3","slug":"executor-was-closed","errorCode":null,"errorMessage":"Executor was closed.","messagePattern":"Executor was closed\\.","errorType":"exception","errorClass":"ExecutorException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/executor/BaseExecutor.java","lineNumber":79,"sourceCode":"  protected Configuration configuration;\n\n  protected int queryStack;\n  private boolean closed;\n\n  protected BaseExecutor(Configuration configuration, Transaction transaction) {\n    this.transaction = transaction;\n    this.deferredLoads = new ConcurrentLinkedQueue<>();\n    this.localCache = new PerpetualCache(\"LocalCache\");\n    this.localOutputParameterCache = new PerpetualCache(\"LocalOutputParameterCache\");\n    this.closed = false;\n    this.configuration = configuration;\n    this.wrapper = this;\n  }\n\n  @Override\n  public Transaction getTransaction() {\n    if (closed) {\n      throw new ExecutorException(\"Executor was closed.\");\n    }\n    return transaction;\n  }\n\n  @Override\n  public void close(boolean forceRollback) {\n    try {\n      try {\n        rollback(forceRollback);\n      } finally {\n        if (transaction != null) {\n          transaction.close();\n        }\n      }\n    } catch (SQLException e) {\n      // Ignore. There's nothing that can be done at this point.\n      log.warn(\"Unexpected exception on closing transaction.  Cause: \" + e);\n    } finally {","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/executor/BaseExecutor.java#L61-L97","documentation":"BaseExecutor guards every public operation with a 'closed' flag set by close(boolean). getTransaction() throws ExecutorException 'Executor was closed.' if called after the executor was closed. Executors are closed when the owning SqlSession closes (or a Spring-managed session ends), so this usually means the SqlSession lifecycle ended before the code touched getTransaction().","triggerScenarios":"Calling executor.getTransaction() (directly, or via a plugin/framework that inspects the executor) after sqlSession.close(); keeping an Executor reference beyond its session's lifetime; accessing the executor from a lazily-run callback (lazy loading, async task) after the session closed.","commonSituations":"Custom MyBatis plugins/interceptors that grab the executor and use it later; storing an Executor in a field or cache; lazy-loading triggers firing after the session was closed; multi-threaded use where one thread closes while another reads the transaction.","solutions":["Perform all executor work within the SqlSession's lifetime — get the transaction while the session is open","If you need the Connection/Transaction, obtain it from the open SqlSession (sqlSession.getConnection()) inside the same scope","Check executor.isClosed() before calling if lifecycle is uncertain","Fix lazy-loading configuration so deferred loads resolve before session close (aggressiveLazyLoading or fetching eagerly)"],"exampleFix":"// before\nExecutor exec = session.getConfiguration()... ; // retained reference\nsession.close();\nTransaction t = exec.getTransaction(); // ExecutorException\n\n// after\ntry (SqlSession session = factory.openSession()) {\n  Connection c = session.getConnection(); // safe: session open\n  // work...\n}","handlingStrategy":"validation","validationCode":"if (!executor.isClosed()) {\n  Transaction tx = executor.getTransaction();\n}","typeGuard":null,"tryCatchPattern":"try {\n  return executor.getTransaction();\n} catch (ExecutorException e) {\n  if (\"Executor was closed.\".equals(e.getMessage())) {\n    // session ended: open a new SqlSession for further work\n  } else throw e;\n}","preventionTips":["Never retain an Executor beyond its SqlSession's lifetime","Use sqlSession.getConnection() inside the open scope instead of touching the executor's transaction","Resolve lazy loads before the session closes"],"tags":["mybatis","executor","lifecycle","session-closed"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}