karatelabs/karate · error · RuntimeException
ext global '' collides with built-in ''
Error message
ext global '' collides with built-in ''
What it means
Suite.registerGlobal(String, Object) rejects names that collide with Karate's built-in globals (karate, read, match, driver, etc., per RESERVED_GLOBAL_NAMES). The RuntimeException names both the attempted ext global and the built-in it would shadow. This protects script resolution: a user global must never silently override a built-in.
Solutions
- Rename the global to something not in RESERVED_GLOBAL_NAMES (e.g. "myKarate" instead of "karate") and update script references
- Check Suite.RESERVED_GLOBAL_NAMES in karate-core to see the full list of taken names
- If registering many globals from a map, filter or prefix reserved keys before the loop
Example fix
// before
suite.registerGlobal("read", new CustomReader());
// after
suite.registerGlobal("customRead", new CustomReader()); Defensive patterns
Strategy: validation
Validate before calling
java.util.Set<String> reserved = java.util.Set.of("karate","read","match","driver");
if (reserved.contains(name)) throw new IllegalArgumentException("name collides with built-in: " + name); Try / catch
try { suite.registerGlobal(name, x); } catch (RuntimeException e) { /* collision: choose another name */ } Prevention
- Prefix user globals (e.g. 'my' or project code)
- Check RESERVED_GLOBAL_NAMES when upgrading Karate
- Avoid registering from unfiltered config key maps
When it happens
Trigger: Calling suite.registerGlobal("karate", x), suite.registerGlobal("read", x), etc. — any name contained in Suite.RESERVED_GLOBAL_NAMES. The template message shows the same name substituted twice ('ext global 'X' collides with built-in 'X'').
Common situations: Registering a helper class but passing the name "karate"; a generic registration loop that iterates over a map whose keys include reserved words; choosing short convenient names that happen to be built-ins after a Karate upgrade added new reserved names.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- registerGlobal: name is null or empty
- at least one feature file is required
- boot.classpath(' '): expected a directory RELATIVE to the…
- boot.classpath: dir is null — pass a project-relative…
- boot.read: path is null
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/f4b229377e9a40ce.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/core/Suite.java:759
// `match` is intentionally NOT reserved — core no longer ships a global `match` (assertions are the
// Gherkin `match` keyword + `karate.match`); a consumer may register its own `match` global.
private static final Set<String> RESERVED_GLOBAL_NAMES = Set.of("karate", "read", "driver");
/**
* Register an ext global under {@code name}, seeded into every scenario's JS
* scope before {@code karate-config.js} evaluates. Called by an {@link Ext}
* from {@link Ext#onBoot(Suite)}; the instance is typically a
* {@link io.karatelabs.js.SimpleObject} so members cross into JS natively
* (no reflection). Throws — and so fails the Suite at boot — when the name is
* blank or collides with a built-in global ({@code karate}, {@code read},
* {@code match}, {@code driver}).
*/
public void registerGlobal(String name, Object instance) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("registerGlobal: name is null or empty");
}
if (RESERVED_GLOBAL_NAMES.contains(name)) {
throw new RuntimeException("ext global '" + name + "' collides with built-in '" + name + "'");
}
globals.put(name, instance);
}
/**
* Register a <em>per-scenario</em> ext global: the {@link ExtGlobalFactory} is
* invoked once per scenario at seed time to mint a fresh instance bound with the
* scenario's {@link KarateJsContext}. Prefer this over the singleton
* {@link #registerGlobal(String, Object)} when the global carries per-scenario
* mutable state or needs runtime / path access — see {@link ExtGlobalFactory}.
*/
public void registerGlobal(String name, ExtGlobalFactory factory) {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("registerGlobal: name is null or empty");
}
if (RESERVED_GLOBAL_NAMES.contains(name)) {
throw new RuntimeException("ext global '" + name + "' collides with built-in '" + name + "'");
}View on GitHub (pinned to a22eb90246)