karatelabs/karate · error · RuntimeException

karate.call() requires a feature context

Error message

karate.call() requires a feature context

What it means

executeJsCall implements karate.call() from JS, resolving a feature path (with optional tag selector and line filter) and invoking it against the current FeatureRuntime. A feature context is mandatory — the called feature runs as a child FeatureRuntime of the caller's. When featureRuntime is null, this RuntimeException is thrown.

Solutions

  1. Move the karate.call() usage into scenario or Background JS within a running feature.
  2. In karate-config.js, replace karate.call() with configure/variable assignment or use it only for pure JS helpers.
  3. If suite-level reuse is needed, call the feature at scenario level once and pass its results down instead of calling from callSingle JS.
  4. Ensure the caller feature is launched through Runner.path(...) so a FeatureRuntime exists.

Example fix

// before: inside karate.callSingle() JS
var result = karate.call('classpath:helpers/lookup.feature');

// after: inside a scenario
* def result = call read('classpath:helpers/lookup.feature')
Defensive patterns

Strategy: type-guard

Type guard

// prefer the keyword inside features; reserve karate.call() for scenario JS
// * def result = call read('classpath:lookup.feature') { id: 1 }

Try / catch

try {
    var result = karate.call('classpath:lookup.feature', { id: 1 });
} catch (e) {
    if (String(e).indexOf('requires a feature context') >= 0) {
        karate.log('karate.call outside feature scope');
        throw e; // structural misuse — do not swallow
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling karate.call('classpath:...') from JS that is not executing inside a feature — e.g. karate.callSingle() cached JS, karate-config.js evaluation, or a standalone script executed through the Karate JS engine outside a feature run.

Common situations: Wrapping feature calls in shared utility JS used by both callSingle and scenarios; calling features from karate-config.js during config init; using karate.call() in setup hooks that predate the feature runtime.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12). Data as JSON: /api/errors/6c59c1171852095f. Report an issue: GitHub.

Appendix: source

Thrown at karate-core/src/main/java/io/karatelabs/core/ScenarioRuntime.java:579

            return new HashMap<>(result);
        }
    }

    /**
     * Execute a feature via karate.call() and return its result variables.
     * This is used for JavaScript calls like: karate.call('other.feature', { arg: 'value' })
     *
     * Supports call-by-tag syntax:
     * - call('file.feature@name=tagvalue') - call feature, run only scenario with matching tag
     * - call('@tagname') - call scenario in same file by tag
     *
     * When arg is a List, loops over elements (same as `call read('path') array`) and
     * returns a List of result maps.
     */
    @SuppressWarnings("unchecked")
    public Object executeJsCall(String path, Object arg) {
        if (featureRuntime == null) {
            throw new RuntimeException("karate.call() requires a feature context");
        }

        // Parse path, tag selector and line filter — shared parser keeps JS
        // (karate.call) and keyword (`call read(...)`) syntax in lockstep.
        StepUtils.ParsedFeaturePath parsed = StepUtils.parseFeaturePath(path);
        String tagSelector = parsed.tagSelector();
        Set<Integer> lineFilters = parsed.lineFilters();
        Feature calledFeature;
        if (parsed.sameFile()) {
            // call('@tagname') — scenario in the same file
            calledFeature = featureRuntime.getFeature();
        } else {
            Resource calledResource = featureRuntime.resolve(parsed.path());
            // A .js target is a callable helper, not a feature — evaluate and invoke it,
            // mirroring read()/callSingle and the `call` keyword (and v1, which dispatched
            // on the resolved type). Blindly parsing it as a feature silently returned an
            // empty result, dropping the helper's functions.
            if ("js".equals(calledResource.getExtension())) {

View on GitHub (pinned to a22eb90246)