karatelabs/karate · error · RuntimeException

karate.setup() requires a feature context

Error message

karate.setup() requires a feature context

What it means

executeSetup runs the @setup-tagged scenario of the current feature (karate.setup()). It requires a FeatureRuntime context — the enclosing feature being executed by the Karate runner. When ScenarioCaller/JS code calls karate.setup() outside a feature execution (featureRuntime is null), this RuntimeException is thrown.

Solutions

  1. Call karate.setup() only from within a running feature's scenario or JS executed there.
  2. If the code is shared via karate.callSingle(), refactor to not use setup (return data directly from the JS function).
  3. If you control the caller, ensure featureRuntime is attached by running through a feature (Runner.path(...)) rather than ad-hoc JS evaluation.
  4. Check for accidental API confusion: karate.call() / karate.setupOnce() have similar context requirements — verify you meant setup().

Example fix

// before: karate-config.js or callSingle JS
var data = karate.setup('makeData');

// after: call setup inside a scenario / feature-scoped JS
Scenario:
  * def data = karate.setup('makeData')
Defensive patterns

Strategy: type-guard

Type guard

function canSetup() {
  // setup needs a feature context; only call it from scenario-scoped JS
  return typeof karate.setup === 'function' && karate.runtime != null;
}
// usage inside a scenario:
// if (canSetup()) karate.setup('makeData');

Try / catch

try {
    var data = karate.setup('makeData');
} catch (e) {
    if (String(e).indexOf('requires a feature context') >= 0) {
        karate.log('setup called outside feature scope; using defaults');
        data = defaultData;
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling karate.setup() (optionally with a name) from JS code that is not running inside a feature's scenario — e.g. from callSingle() cached code, a standalone JS script, or a config script evaluated before the feature runtime is attached.

Common situations: Using karate.setup() inside karate.callSingle() cached JS, which runs at suite level with no feature context; invoking setup helpers from karate-config.js; refactored JS moved from a Scenario into shared JS executed out of context.

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/1715ddc2009ab5bb. Report an issue: GitHub.

Appendix: source

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

                }
                logger.debug("Evaluated {}: {} variables", displayName, vars.size());
                return vars;
            } else if (result != null) {
                logger.warn("{} did not return an object, got: {}", displayName, result.getClass().getSimpleName());
            }
            return null;
        } catch (Exception e) {
            logger.warn("Failed to evaluate {}: {}", displayName, e.getMessage());
            throw new RuntimeException("Config evaluation failed: " + displayName + " - " + e.getMessage(), e);
        }
    }

    /**
     * Execute the @setup scenario and return all its variables.
     */
    public Map<String, Object> executeSetup(String name) {
        if (featureRuntime == null) {
            throw new RuntimeException("karate.setup() requires a feature context");
        }
        Scenario setupScenario = scenario.getFeature().getSetup(name);
        if (setupScenario == null) {
            String message = "no scenario found with @setup tag";
            if (name != null) {
                message = message + " and name '" + name + "'";
            }
            throw new RuntimeException(message);
        }
        // Run the setup scenario without background
        ScenarioRuntime sr = new ScenarioRuntime(featureRuntime, setupScenario);
        sr.setSkipBackground(true);
        sr.call();
        // The setup runtime re-evaluates config, and a failure there is now captured rather than
        // thrown from the constructor. call()'s failed result is discarded here, so without this the
        // caller gets an empty variable map and the config error resurfaces as a baffling
        // "x is not defined" wherever a setup variable was expected. Fail where the cause is.
        Throwable setupConfigError = sr.getConfigError();

View on GitHub (pinned to a22eb90246)