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

  1. Read `t.toString()` in the log to find the error inside your hook
  2. Make the hook defensive: null-check fields of the `info` map before use
  3. Move side effects (alerting, logging) into try/catch inside the hook itself
  4. 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

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


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)