{"record":{"id":"bcdb25f7ef614024","repo":"karatelabs/karate","slug":"karate-setuponce-requires-a-feature-context","errorCode":null,"errorMessage":"karate.setupOnce() requires a feature context","messagePattern":"karate\\.setupOnce\\(\\) requires a feature context","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"karate-core/src/main/java/io/karatelabs/core/ScenarioRuntime.java","lineNumber":544,"sourceCode":"        sr.call();\n        // The setup runtime re-evaluates config, and a failure there is now captured rather than\n        // thrown from the constructor. call()'s failed result is discarded here, so without this the\n        // caller gets an empty variable map and the config error resurfaces as a baffling\n        // \"x is not defined\" wherever a setup variable was expected. Fail where the cause is.\n        Throwable setupConfigError = sr.getConfigError();\n        if (setupConfigError != null) {\n            throw setupConfigError instanceof RuntimeException re ? re : new RuntimeException(setupConfigError);\n        }\n        return sr.getAllVariables();\n    }\n\n    /**\n     * Execute the @setup scenario with caching (only runs once per feature).\n     */\n    @SuppressWarnings(\"unchecked\")\n    public Map<String, Object> executeSetupOnce(String name) {\n        if (featureRuntime == null) {\n            throw new RuntimeException(\"karate.setupOnce() requires a feature context\");\n        }\n        String cacheKey = name == null ? \"__default__\" : name;\n        Map<String, Object> setupCache = featureRuntime.getSetupOnceCache();\n        Map<String, Object> cached = (Map<String, Object>) setupCache.get(cacheKey);\n        if (cached != null) {\n            // Return a shallow copy to prevent modifications affecting other scenarios\n            return new HashMap<>(cached);\n        }\n        synchronized (setupCache) {\n            // Double-check after acquiring lock\n            cached = (Map<String, Object>) setupCache.get(cacheKey);\n            if (cached != null) {\n                return new HashMap<>(cached);\n            }\n            Map<String, Object> result = executeSetup(name);\n            setupCache.put(cacheKey, result);\n            return new HashMap<>(result);\n        }","sourceCodeStart":526,"sourceCodeEnd":562,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-core/src/main/java/io/karatelabs/core/ScenarioRuntime.java#L526-L562","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Invoke karate.setupOnce() only from scenario-scoped JS inside a running feature.","For suite-level caching use karate.callSingle() with plain JS instead of setupOnce.","Ensure the feature is executed via the Runner (not ad-hoc JS evaluation) so the FeatureRuntime is created.","If the intent is one-time setup per feature, place karate.setupOnce() in a Background section instead."],"exampleFix":"// before: inside callSingle JS\nvar fn = karate.callSingle('data-setup.js');\nvar data = karate.setupOnce('seed');\n\n// after: inside the feature\nBackground:\n  * def data = karate.setupOnce('seed')","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// call setupOnce only from scenario scope\nfunction setupOnceSafe(name) {\n  try { return karate.setupOnce(name); }\n  catch (e) {\n    if (String(e).indexOf('requires a feature context') >= 0) return null;\n    throw e;\n  }\n}","tryCatchPattern":"try {\n    var data = karate.setupOnce('seed');\n} catch (e) {\n    if (String(e).indexOf('requires a feature context') >= 0) {\n        karate.log('setupOnce used outside feature; skipping cache');\n        data = karate.call('classpath:seed.feature');\n    } else { throw e; }\n}","preventionTips":["Use setupOnce only inside features (Background is a good place)","Use karate.callSingle() for suite-level one-time data instead","Don't nest setupOnce inside callSingle-cached JS","Verify features run through the Runner so FeatureRuntime is attached"],"tags":["setup","caching","karate-js","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}