mybatis/mybatis-3 · error · SQLException

SqlRunner could not find a TypeHandler instance for {}

Error message

SqlRunner could not find a TypeHandler instance for {}

What it means

SqlRunner.setParameters() throws SQLException when TypeHandlerRegistry has no TypeHandler registered for the concrete class of an argument. SqlRunner resolves handlers purely from args[i].getClass(), so any type outside the built-in set (primitives, dates, strings, enums, etc.) and not user-registered fails here.

Source

Thrown at src/main/java/org/apache/ibatis/jdbc/SqlRunner.java:227

    try {
      connection.close();
    } catch (SQLException e) {
      // ignore
    }
  }

  private void setParameters(PreparedStatement ps, Object... args) throws SQLException {
    for (int i = 0, n = args.length; i < n; i++) {
      if (args[i] == null) {
        throw new SQLException(
            "SqlRunner requires an instance of Null to represent typed null values for JDBC compatibility");
      }
      if (args[i] instanceof Null) {
        ((Null) args[i]).getTypeHandler().setParameter(ps, i + 1, null, ((Null) args[i]).getJdbcType());
      } else {
        TypeHandler typeHandler = typeHandlerRegistry.getTypeHandler(args[i].getClass());
        if (typeHandler == null) {
          throw new SQLException("SqlRunner could not find a TypeHandler instance for " + args[i].getClass());
        } else {
          typeHandler.setParameter(ps, i + 1, args[i], null);
        }
      }
    }
  }

  private List<Map<String, Object>> getResults(ResultSet rs) throws SQLException {
    List<Map<String, Object>> list = new ArrayList<>();
    List<String> columns = new ArrayList<>();
    List<TypeHandler<?>> typeHandlers = new ArrayList<>();
    ResultSetMetaData rsmd = rs.getMetaData();
    for (int i = 0, n = rsmd.getColumnCount(); i < n; i++) {
      columns.add(rsmd.getColumnLabel(i + 1));
      try {
        Class<?> type = Resources.classForName(rsmd.getColumnClassName(i + 1));
        TypeHandler<?> typeHandler = typeHandlerRegistry.getTypeHandler(type);
        if (typeHandler == null) {

View on GitHub (pinned to 008069adb1)

Solutions

  1. Convert the value to a supported JDBC type before passing it (e.g. uuid.toString(), Year.getValue()).
  2. Register a handler on the Configuration SqlRunner uses: typeHandlerRegistry.register(MyType.class, new MyTypeHandler()) before constructing SqlRunner.
  3. Prefer the normal mapper API (which maps types via parameter xmlType/resultSet metadata) for non-standard types instead of SqlRunner.

Example fix

// before
runner.update("insert into t(uuid_col) values (?)", java.util.UUID.randomUUID());

// after
runner.update("insert into t(uuid_col) values (?)", java.util.UUID.randomUUID().toString());
Defensive patterns

Strategy: validation

Validate before calling

// Confirm a handler exists before running
TypeHandler<?> h = configuration.getTypeHandlerRegistry().getTypeHandler(MyType.class);
if (h == null) {
  configuration.getTypeHandlerRegistry().register(MyType.class, new MyTypeHandler());
}
SqlRunner runner = new SqlRunner(configuration, conn);

Prevention

When it happens

Trigger: Passing an exotic argument class to SqlRunner methods: java.util.UUID (older MyBatis versions), java.time Year/Period, a custom value object, or a byte[] subclass, where no handler exists in the registry.

Common situations: Using SqlRunner in tests with java.time or custom types not registered; a TypeHandler registered on a Configuration different from the one SqlRunner was constructed with; version upgrades that change default handler coverage.

Related errors


AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14). Data as JSON: /api/errors/66f452b677c2874e. Report an issue: GitHub.