mybatis/mybatis-3 · error · IOException

Could not find resource {}

Error message

Could not find resource {}

What it means

Resources.getResourceURL asked ClassLoaderWrapper for a classpath resource across the full classloader chain and every loader returned null, so it throws IOException 'Could not find resource'. This is the URL variant used when a real URL (not a stream) is required, e.g. by the XML config/mapper loaders.

Source

Thrown at src/main/java/org/apache/ibatis/io/Resources.java:98

  }

  /**
   * Returns the URL of the resource on the classpath
   *
   * @param loader
   *          The classloader used to fetch the resource
   * @param resource
   *          The resource to find
   *
   * @return The resource
   *
   * @throws java.io.IOException
   *           If the resource cannot be found or read
   */
  public static URL getResourceURL(ClassLoader loader, String resource) throws IOException {
    URL url = classLoaderWrapper.getResourceAsURL(resource, loader);
    if (url == null) {
      throw new IOException("Could not find resource " + resource);
    }
    return url;
  }

  /**
   * Returns a resource on the classpath as a Stream object
   *
   * @param resource
   *          The resource to find
   *
   * @return The resource
   *
   * @throws java.io.IOException
   *           If the resource cannot be found or read
   */
  public static InputStream getResourceAsStream(String resource) throws IOException {
    return getResourceAsStream(null, resource);
  }

View on GitHub (pinned to 008069adb1)

Solutions

  1. Use a classpath-root-relative path without leading slash: 'mybatis-config.xml', 'mappers/UserMapper.xml' — never 'src/main/resources/...'.
  2. Verify the file is in target/classes (or the deployed jar) after a clean build.
  3. Check path case exactly matches the file name on case-sensitive filesystems.
  4. Pass an explicit ClassLoader (Resources.getResourceURL(loader, path)) in container/boot environments where the context loader cannot see app classes.

Example fix

// before
URL url = Resources.getResourceURL("src/main/resources/mybatis-config.xml");

// after
URL url = Resources.getResourceURL("mybatis-config.xml");
Defensive patterns

Strategy: validation

Validate before calling

// verify resource existence with a clear error before MyBatis loads it
static URL requireResource(ClassLoader cl, String path) {
  URL url = cl == null ? Thread.currentThread().getContextClassLoader().getResource(path) : cl.getResource(path);
  if (url == null) throw new IllegalStateException("Missing classpath resource (expected at classpath root, no leading '/'): " + path);
  return url;
}

Type guard

static boolean resourceExists(ClassLoader cl, String path) {
  return (cl != null ? cl : Thread.currentThread().getContextClassLoader()).getResource(path) != null;
}

Prevention

When it happens

Trigger: SqlSessionFactoryBuilder.build(reader) is absent — instead config_LOCATION style APIs: Resources.getResourceURL("mybatis-config.xml") or mybatis-config loader resolving <mapper resource="..."> / <sqlMap ...> entries where the path is wrong relative to the classpath root.

Common situations: Leading slash or 'src/main/resources' prefix in the path; file placed under a package instead of the root; resource in a jar not on the runtime classpath; case-sensitivity on Linux; IDE build not copying resources; webapp deployments with non-standard classloaders.

Related errors


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