karatelabs/karate · warning
hook failed
Error message
{} hook failed: {} What it means
A lifecycle hook (before/after mock request hook) registered on the mock server threw an exception when invoked. Karate catches it, logs a warning, and by default converts it into a 500 response via hookErrorResponse so the mocked endpoint still answers. It indicates a bug or unexpected condition inside user-supplied hook code, not in Karate itself.
Solutions
- Read the second '{}' value in the log — e.getMessage() of the hook exception — and fix the bug in the hook code.
- Wrap the hook body in defensive null checks / try-catch so it never throws for edge-case requests.
- If the hook failure should be tolerated, make the hook catch its own exceptions and return a fallback response.
- Check that the hook returns/behaves correctly for all HTTP verbs and paths the mock receives.
Example fix
// before
fn = function(req){ return { status: req.body.length } }
// after
fn = function(req){ if (!req.body) return { status: 400 }; try { return { status: req.body.length } } catch (e) { return { status: 500, message: e.message } } } Defensive patterns
Strategy: try-catch
Validate before calling
// ensure hook is a function and deps exist before registering
if (typeof hookFn !== 'function') throw new Error('hook must be a function'); Type guard
function isValidHook(h) { return h && typeof h === 'function'; } Try / catch
try { hook.call(null); } catch (e) { logger.warn('hook failed: {}', e.getMessage()); /* tolerate or return 500 via hookErrorResponse */ } Prevention
- Write hooks defensively: null-check request fields before use.
- Cover hooks with unit tests across all HTTP verbs/paths.
- Keep hook logic minimal — delegate to tested functions.
When it happens
Trigger: A hook object passed to MockHandler (mock before/after hooks) throws when hook.call(null) is executed during request processing, via invokeMockHook called from beforeError/afterError handling.
Common situations: Hook script references a variable that does not exist; hook JS/Java code throws NPE; hook depends on request state that is absent for certain HTTP methods; a refactor renamed a variable the hook uses.
Related errors
- proceed() can only be called within a mock scenario
- proceed() needs a target URL or Host header in request
- scenario match evaluation failed at line
- start() argument must be a string path or config map
- start() config requires 'mock' key with feature path
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/2c8a4bd646a89f6c.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/core/MockHandler.java:531
// A hook exception surfaces as HTTP 500 (same convention as beforeScenario / step failures).
Exception afterError = invokeMockHook(config.getAfterScenario(), "afterScenario");
if (afterError != null) {
return hookErrorResponse("afterScenario", afterError);
}
// Build response from variables
return buildResponse(runtime, engine, request);
}
private Exception invokeMockHook(JavaCallable hook, String hookName) {
if (hook == null) {
return null;
}
try {
hook.call(null);
return null;
} catch (Exception e) {
logger.warn("{} hook failed: {}", hookName, e.getMessage());
return e;
}
}
private HttpResponse hookErrorResponse(String hookName, Exception error) {
return HttpResponse.error(500, hookName + " hook failed: " + error.getMessage());
}
@SuppressWarnings("unchecked")
private HttpResponse buildResponse(ScenarioRuntime runtime, Engine engine, HttpRequest request) {
HttpResponse response = new HttpResponse();
// Get response variables from engine
Object responseBody = engine.get("response");
Object responseStatus = engine.get("responseStatus");
Object responseStatusText = engine.get("responseStatusText");
Object responseHeaders = engine.get("responseHeaders");
Object responseDelay = engine.get("responseDelay");View on GitHub (pinned to a22eb90246)