karatelabs/karate · error · IOException
ext ' ': resource vanished after validation
Error message
ext '<name>': resource vanished after validation: <path>
What it means
During report generation, ReportAssets.copyResource finds that a classpath resource which passed earlier validation can no longer be opened. This is an IOException thrown mid-copy, indicating the classpath changed between validation and copy (or the resource was never addressable through this classloader).
Solutions
- Re-run validateAndBind / restart the Suite after any rebuild so validation and copy use the same classpath
- Confirm the resource exists in the built artifact: jar tf app.jar | grep <path>
- Use the same ClassLoader for validation and copy (e.g. Thread.currentThread().getContextClassLoader())
- Ensure build config (Maven/Gradle resources) includes the report assets directory
Example fix
// before (validate with one loader, copy with another) assets.validateAndBind(MyApp.class.getClassLoader()); // after ClassLoader cl = Thread.currentThread().getContextClassLoader(); assets.validateAndBind(cl); // ... later, copyTo must use the same cl
Defensive patterns
Strategy: fallback
Validate before calling
boolean present(ClassLoader cl, String path) { return cl.getResource(BASE + path) != null; } Try / catch
try { assets.copyTo(targetDir); } catch (IOException e) { if (e.getMessage().contains("resource vanished")) { log.warn("classpath changed mid-run; revalidating"); assets.validateAndBind(cl); assets.copyTo(targetDir); } else throw e; } Prevention
- Avoid rebuilding/cleaning the output directory while a suite is running
- Use a single, stable ClassLoader for validation and copying
- Verify resources survive into the packaged jar (shade plugin includes)
When it happens
Trigger: copyTo() copying a previously validated js/css/asset whose getResourceAsStream now returns null — e.g. jar rebuilt or classloader swapped between validateAndBind and report generation.
Common situations: Fat-jar shading that strips resources; the resource living in a different jar not on the runtime classpath; hot-reload/dev mode where target/classes was cleaned mid-run; module/classloader isolation (app server) hiding the resource from this ClassLoader.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- ext ' ': points at missing resource
- ext ' ': failed reading
- Failed to load icons sprite
- Failed to externalize embeds
- Failed to copy ext assets for
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/4cf21dab92403856.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/core/ReportAssets.java:211
Files.createDirectories(extDir);
copyResource(js, extDir.resolve(stripStatic(js)));
if (css != null) {
copyResource(css, extDir.resolve(stripStatic(css)));
}
for (String a : assets) {
copyResource(a, extDir.resolve(stripStatic(a)));
}
for (Page p : pages) {
if (p.href() != null) {
copyResource(p.href(), extDir.resolve(p.href()));
}
}
}
private void copyResource(String path, Path target) throws IOException {
try (InputStream is = classLoader.getResourceAsStream(BASE + path)) {
if (is == null) {
throw new IOException("ext '" + name + "': resource vanished after validation: " + path);
}
Path parent = target.getParent();
if (parent != null) {
Files.createDirectories(parent);
}
Files.copy(is, target, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
}
}
private void requireResource(ClassLoader cl, String path, String key) {
try (InputStream is = cl.getResourceAsStream(BASE + path)) {
if (is == null) {
throw new RuntimeException("ext '" + name + "': " + key
+ " points at missing resource: " + BASE + path);
}
} catch (IOException e) {
throw new RuntimeException("ext '" + name + "': failed reading " + BASE + path + " — " + e.getMessage(), e);
}View on GitHub (pinned to a22eb90246)