karatelabs/karate · error · RuntimeException
karate.driver can only be read within a scenario
Error message
karate.driver can only be read within a scenario
What it means
The karate.driver root binding lazily initializes the active browser driver by asking the current ScenarioRuntime. Outside a scenario (e.g. in a boot script, JS function eval before/after a feature, or a standalone JS context) there is no runtime, so reading karate.driver throws instead of returning a useless null.
Solutions
- Only access karate.driver inside a running scenario (Scenario/Background/JS within a scenario)
- Move driver-dependent logic from boot/config scripts into scenario steps
- In shared JS, guard with karate.runtime info or pass the driver in as a parameter instead of reading the global binding.
Example fix
// before (karate-config.js)
config.driver = karate.driver; // throws at boot
// after
config.getDriver = function() { return karate.driver; }; // read lazily inside a scenario Defensive patterns
Strategy: type-guard
Validate before calling
// JS: only inside a scenario; in shared scripts defer the read
var getDriver = function() { return karate.driver; }; Type guard
// JS: cannot query runtime presence directly; wrap reads in functions instead of eager access
function safeDriver() { try { return karate.driver; } catch (e) { return null; } } Try / catch
try { var d = karate.driver; } catch (e) { karate.warn('driver unavailable outside scenario'); } Prevention
- Never read karate.driver in karate-config.js or boot scripts
- Access driver only inside Scenario/Background scope
- Re-read karate.driver after driver.quit() to re-init lazily
- Parameterize shared utility scripts instead of reading global bindings
When it happens
Trigger: Reading `karate.driver` in karate-config.js / boot-time JS, in a called JS-only context with no scenario, or after the scenario runtime has been torn down (e.g. in an afterSuite hook).
Common situations: Shared utility JS scripts that reference karate.driver unconditionally but are also loaded at boot; calling driver operations in background/setup hooks without an active scenario; assuming the driver survives after quit/teardown.
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
- karate.setup() is not available in this context
- render() needs at least one argument
- render() read arg should not be null
- channel() can only be called within a scenario
- karate.setup() requires a feature context
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/d47204655ea25bb9.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/core/KarateJs.java:1188
} catch (Exception e) {
throw new RuntimeException("Failed to write file: " + file.getAbsolutePath(), e);
}
};
}
/**
* JS-side access to the active browser driver, initialising it lazily from
* {@code configure driver = { ... }} on first read — the JS equivalent of the
* {@code * driver ...} step. Useful when driver lifecycle is orchestrated inside
* a JS function (e.g. iterating over a list of browser configs in a grid run),
* where Gherkin steps aren't reachable per iteration. Returns the same instance
* exposed via the {@code driver} root binding; after {@code driver.quit()} a
* subsequent read re-inits cleanly via {@link ScenarioRuntime#getDriver()}.
*/
private io.karatelabs.driver.Driver getDriverLazy() {
ScenarioRuntime rt = getRuntime();
if (rt == null) {
throw new RuntimeException("karate.driver can only be read within a scenario");
}
return rt.getDriver();
}
// ========== Channel Support ==========
private JavaInvokable channel() {
return args -> {
if (args.length == 0) {
throw new RuntimeException("channel() needs a type argument, e.g. karate.channel('kafka')");
}
String type = args[0].toString();
ScenarioRuntime rt = getRuntime();
if (rt == null) {
throw new RuntimeException("channel() can only be called within a scenario");
}
// An ext (e.g. boot.ext('grpc')) may have registered a factory on the Suite at boot;
// it wins over the name-convention fallback. See Suite#registerChannelFactory.View on GitHub (pinned to a22eb90246)