mybatis/mybatis-3 · error · ExecutorException
Unknown statement type: {}
Error message
Unknown statement type: {} What it means
RoutingStatementHandler switches on mappedStatement.getStatementType() and knows only STATEMENT, PREPARED, and CALLABLE. Any other value falls through to 'Unknown statement type'. With the stock StatementType enum this is defensive dead code; in practice it appears when a custom/modified enum, a different MyBatis fork, or a corrupted serialization of MappedStatement introduces an unexpected value.
Source
Thrown at src/main/java/org/apache/ibatis/executor/statement/RoutingStatementHandler.java:53
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
@Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
}
@Override
public void batch(Statement statement) throws SQLException {
delegate.batch(statement);
}View on GitHub (pinned to 008069adb1)
Solutions
- Align all MyBatis artifacts to one version (check mvn dependency:tree for mybatis* artifacts).
- Remove duplicate/forked mybatis jars from the classpath so a single StatementType enum is used.
- If you maintain a fork adding a statement type, extend RoutingStatementHandler's switch accordingly.
- Do not cache or transport MappedStatement objects across different MyBatis versions.
Defensive patterns
Strategy: validation
Validate before calling
// startup check: exactly one mybatis core on the classpath
Enumeration<URL> jars = getClass().getClassLoader().getResources("org/apache/ibatis/session/Configuration.class");
List<URL> found = Collections.list(jars);
if (found.size() != 1) {
throw new IllegalStateException("Multiple/split mybatis classes found: " + found);
} Prevention
- Use Maven enforcer (bannedDependencies/duplicateClasses) or Gradle version alignment to keep one mybatis artifact.
- Never pass MappedStatement objects between processes or versions.
When it happens
Trigger: Constructing a RoutingStatementHandler for a MappedStatement whose statementType is not one of the three known enum constants — e.g. custom StatementType extension, MappedStatement deserialized from an incompatible version, or shaded/duplicated mybatis jars with diverging enums.
Common situations: Mixing MyBatis versions on the classpath (mybatis + mybatis-spring or mybatis-plus pulling different versions); caching/serializing MappedStatement across versions; custom source forks adding a new statement type without updating RoutingStatementHandler.
Related errors
- Error setting driver on UnpooledDataSource.
- Cannot enable lazy loading because CGLIB is not available. A
- Cannot enable lazy loading because Javassist is not availabl
- Error cloning cache key. Cause: {}
- Cannot find class: {}
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/1874cfe3d7959818.
Report an issue: GitHub.