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

  1. Rename the global to something not in RESERVED_GLOBAL_NAMES (e.g. "myKarate" instead of "karate") and update script references
  2. Check Suite.RESERVED_GLOBAL_NAMES in karate-core to see the full list of taken names
  3. 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

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


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)