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
- Use a classpath-root-relative path without leading slash: 'mybatis-config.xml', 'mappers/UserMapper.xml' — never 'src/main/resources/...'.
- Verify the file is in target/classes (or the deployed jar) after a clean build.
- Check path case exactly matches the file name on case-sensitive filesystems.
- 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
- Reference resources classpath-root-relative without leading slashes or src/main/resources prefixes.
- Add a bootstrap assertion that key config/mapper resources resolve, so packaging mistakes fail at startup.
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
- Error setting driver on UnpooledDataSource.
- Cannot find class: {}
- Error setting Log implementation. Cause: {}
- Could not resolve type alias '{}'. Cause: {}
- Error registering type alias {} for {}. Cause: {}
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/4d8b8b8b5b822814.
Report an issue: GitHub.