karatelabs/karate · warning
onStepFailure hook threw, continuing
Error message
onStepFailure hook threw, continuing: {} What it means
Karate invokes the user-supplied `configure onStepFailure` hook with a map of failure info after each failed step. If the hook callable itself throws, Karate logs this warning and continues the scenario; the hook's exception never masks the original failure.
Solutions
- Read `t.toString()` in the log to find the error inside your hook
- Make the hook defensive: null-check fields of the `info` map before use
- Move side effects (alerting, logging) into try/catch inside the hook itself
- Test the hook in isolation with a deliberately failing step
Example fix
// before
configure onStepFailure = function(info){ karate.call('classpath:alert.feature', { id: info.scenarioId.id }) }
// after
configure onStepFailure = function(info){ try { karate.call('classpath:alert.feature', { id: info.scenarioId.id }) } catch(e) { karate.log('alert failed', e) } } Defensive patterns
Strategy: try-catch
Validate before calling
// Inside the hook, validate the info map before use:
configure onStepFailure = function(info){ if (!info || !info.scenarioId) { karate.log('no failure info'); return; } /* ... */ } Type guard
function hasScenarioId(info) { return info != null && info.scenarioId != null; } Try / catch
try { callable.call(null, new Object[]{info}); } catch (Throwable t) { logger.warn("onStepFailure hook threw, continuing: {}", t.toString()); } Prevention
- Null-check every field you read from the failure-info map
- Keep side effects (webhooks) wrapped in their own try/catch
- Unit-test hooks with a deliberately failing step
When it happens
Trigger: A registered `onStepFailure` JS/Java callable throws — e.g. it dereferences missing fields, calls a failing API for alerting, or has a JS syntax/runtime error.
Common situations: Hook reads a field absent from the failure-info map; webhook/alerting call inside the hook fails; typos in hook JS.
Related errors
- hook failed
- append() needs at least two arguments
- appendTo() needs at least two arguments: list and item(s)
- assert expression must return boolean:
- assert failed:
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/295b8f0a1dfe9cfd.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/core/ScenarioRuntime.java:2037
return true;
}
}
/**
* Invoke the {@code configure onStepFailure} hook with a single info-map
* argument. The map exposes failure metadata plus three JS-callable
* methods (embed / proceed / stop) bound to {@code sr} and {@code decision}.
*/
private void invokeOnStepFailureHook(StepResult sr, FailureDecision decision) {
Object hookRef = config.getOnStepFailure();
if (!(hookRef instanceof JavaCallable callable)) {
return;
}
try {
Map<String, Object> info = buildStepFailureInfo(sr, decision);
callable.call(null, new Object[]{info});
} catch (Throwable t) {
logger.warn("onStepFailure hook threw, continuing: {}", t.toString());
}
}
private Map<String, Object> buildStepFailureInfo(StepResult sr, FailureDecision decision) {
Map<String, Object> info = new LinkedHashMap<>();
Throwable err = sr.getError();
info.put("error", err == null ? null : err.getMessage());
Step step = sr.getStep();
if (step != null) {
Map<String, Object> stepInfo = new LinkedHashMap<>();
stepInfo.put("line", step.getLine());
stepInfo.put("text", step.getText());
stepInfo.put("prefix", step.getPrefix());
info.put("step", stepInfo);
}
info.put("scenarioName", scenario == null ? null : scenario.getName());
if (scenario != null && scenario.getFeature() != null) {
info.put("featureName", scenario.getFeature().getName());View on GitHub (pinned to a22eb90246)