karatelabs/karate · error · RuntimeException

karate.setupOnce() requires a feature context

Error message

karate.setupOnce() requires a feature context

What it means

executeSetupOnce is the cached variant of setup (karate.setupOnce()), running an @setup scenario once per FeatureRuntime. Like karate.setup(), it requires an active feature context; if featureRuntime is null, this RuntimeException is thrown. The cache lookup happens only after this guard passes.

Solutions

  1. Invoke karate.setupOnce() only from scenario-scoped JS inside a running feature.
  2. For suite-level caching use karate.callSingle() with plain JS instead of setupOnce.
  3. Ensure the feature is executed via the Runner (not ad-hoc JS evaluation) so the FeatureRuntime is created.
  4. If the intent is one-time setup per feature, place karate.setupOnce() in a Background section instead.

Example fix

// before: inside callSingle JS
var fn = karate.callSingle('data-setup.js');
var data = karate.setupOnce('seed');

// after: inside the feature
Background:
  * def data = karate.setupOnce('seed')
Defensive patterns

Strategy: type-guard

Type guard

// call setupOnce only from scenario scope
function setupOnceSafe(name) {
  try { return karate.setupOnce(name); }
  catch (e) {
    if (String(e).indexOf('requires a feature context') >= 0) return null;
    throw e;
  }
}

Try / catch

try {
    var data = karate.setupOnce('seed');
} catch (e) {
    if (String(e).indexOf('requires a feature context') >= 0) {
        karate.log('setupOnce used outside feature; skipping cache');
        data = karate.call('classpath:seed.feature');
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling karate.setupOnce() from JS running outside a feature execution — typically from karate.callSingle() cached code or a suite-level script where featureRuntime is null.

Common situations: Migrating karate.callSingle() JS to use setupOnce assuming a feature exists; invoking setupOnce from karate-config.js before scenarios start; utility JS shared between feature and suite scopes.

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

Appendix: source

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

        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();
        if (setupConfigError != null) {
            throw setupConfigError instanceof RuntimeException re ? re : new RuntimeException(setupConfigError);
        }
        return sr.getAllVariables();
    }

    /**
     * Execute the @setup scenario with caching (only runs once per feature).
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> executeSetupOnce(String name) {
        if (featureRuntime == null) {
            throw new RuntimeException("karate.setupOnce() requires a feature context");
        }
        String cacheKey = name == null ? "__default__" : name;
        Map<String, Object> setupCache = featureRuntime.getSetupOnceCache();
        Map<String, Object> cached = (Map<String, Object>) setupCache.get(cacheKey);
        if (cached != null) {
            // Return a shallow copy to prevent modifications affecting other scenarios
            return new HashMap<>(cached);
        }
        synchronized (setupCache) {
            // Double-check after acquiring lock
            cached = (Map<String, Object>) setupCache.get(cacheKey);
            if (cached != null) {
                return new HashMap<>(cached);
            }
            Map<String, Object> result = executeSetup(name);
            setupCache.put(cacheKey, result);
            return new HashMap<>(result);
        }

View on GitHub (pinned to a22eb90246)