karatelabs/karate · error · RuntimeException
image: failed to read options
Error message
image: failed to read options '{optionsPath}': {message} What it means
When diff() resolves a named baseline it loads the companion options JSON file. A missing options file is silently ignored, but if the file exists and cannot be read or parsed as JSON, loadOptionsFile wraps the failure in a RuntimeException naming the options path and underlying message.
Solutions
- Open the optionsPath printed in the message and validate it is well-formed JSON (run it through a JSON linter)
- Fix file permissions so the test process can read it
- If the options are unnecessary, delete the file — a missing options file is treated as empty
- Re-establish the baseline with image.write so a fresh, valid options file is written
Example fix
// before (options file, invalid)
{ "fuzzy": 0.1, }
// after
{ "fuzzy": 0.1 } Defensive patterns
Strategy: try-catch
Validate before calling
# before calling image.diff, sanity-check the options file * def optsText = karate.readIfExists(optionsPath) || '' * if (optsText != '') eval "JSON.parse(karate.string(optsText))" // throws early on invalid JSON
Try / catch
try { return karate.call('image.diff', { name: name }); } catch (Exception e) { if (('' + e).startsWith('image: failed to read options')) { karate.warn('bad options file, diffing with defaults'); return karate.call('image.diff', { name: name, optionsRemoved: true }); } throw e; } Prevention
- Validate options JSON files in CI (jq or a linter) before test runs
- Never hand-edit generated options files without re-validating
- Write options files atomically to avoid partial reads
- Keep options files ASCII/UTF-8 without BOM or comments
When it happens
Trigger: The <name>.options.json (or similarly resolved optionsPath) exists but contains invalid JSON, is unreadable due to permissions, or the stream fails mid-read while calling image.diff with a name.
Common situations: Hand-edited options file with a trailing comma or comments; options file saved with wrong encoding or as HTML (error page) by a misconfigured process; file locked or chmod'ed 000 in CI; partial file from an interrupted write.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- invalid json: input is null or blank
- invalid json: not a JSON object or array
- invalid json
- Failed to read bytes from
- remove() needs two arguments: variable name and path
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/eb36bd7c217e4d61.
Report an issue: GitHub.
Appendix: source
Thrown at karate-image/src/main/java/io/karatelabs/ext/image/ImageApi.java:384
private String optionsDir() {
String dir = str(config.get("optionsDir"));
return dir != null ? dir : str(config.get("baselineDir"));
}
// ---- options file ----
private Map<String, Object> loadOptionsFile(String optionsPath) {
if (optionsPath == null) {
return new LinkedHashMap<>();
}
Resource r = resolveResource(optionsPath);
if (r == null || !r.exists()) {
return new LinkedHashMap<>(); // missing options is never an error
}
try (InputStream is = r.getStream()) {
return Json.of(new String(is.readAllBytes(), StandardCharsets.UTF_8)).asMap();
} catch (Exception e) {
throw new RuntimeException("image: failed to read options '" + optionsPath + "': " + e.getMessage(), e);
}
}
/** Suite-level engine config passed to {@link ImageComparison} as defaultOptions. */
private Map<String, Object> defaultOptions() {
Map<String, Object> d = new LinkedHashMap<>();
d.put("engine", config.getOrDefault("engine", "resemble"));
if (config.containsKey("threshold")) {
d.put("failureThreshold", config.get("threshold"));
}
d.put("report", config.getOrDefault("report", "mismatched"));
if (config.containsKey("allowScaling")) {
d.put("allowScaling", config.get("allowScaling"));
}
if (config.containsKey("clusters")) {
d.put("clusters", config.get("clusters"));
}
return d;View on GitHub (pinned to a22eb90246)