karatelabs/karate · error · RuntimeException
${formatted template error block (template source…
Error message
${formatted template error block (template source, line/col, cause)} What it means
When template processing fails for any reason other than a 404 ResourceNotFoundException or an intentional flow-control signal (e.g. context.redirect/context.switch), Markup.process logs the failure via logTemplateError — which formats a block with the template source and line/column of the failure — and throws a RuntimeException whose message is that formatted block and whose cause is the original exception. The message is deliberately self-contained so callers like ServerRequestCycle.handleError can display it in devMode without re-deriving diagnostics.
Solutions
- Read the formatted message: it names the template, line/column, and includes the source excerpt — fix the underlying template problem it points to.
- Inspect `getCause()` for the original exception (e.g. the processor's RuntimeException) for the true root cause.
- If it was an intentional flow control (redirect/switch), it should not reach here — verify your flow-control signal isn't wrapped in a way that hides it from hasFlowControlSignal.
- In production mode, rely on logs instead of the response body; keep the cause chain intact when rethrowing.
Example fix
// before (generic handling that hides the formatted block)
} catch (Exception e) {
log.error("render failed");
}
// after (surface the formatted block and root cause)
} catch (RuntimeException e) {
log.error("template render failed:\n{}\nroot cause:", e.getMessage(), e.getCause(), e);
throw e;
} Defensive patterns
Strategy: try-catch
Try / catch
try {
markup.process(...);
} catch (RuntimeException e) {
// e.getMessage() is the formatted template error block; e is the root cause
log.error("Template render failed:\n{}", e.getMessage(), e);
if (!devMode) {
throw new IllegalArgumentException("template render failed (see logs)", e);
}
throw e; // devMode callers can print the formatted block to the response
} Prevention
- Read the formatted message first — it contains template path, line/col and source
- Check getCause() for the underlying processor exception
- Verify flow-control signals (redirect/switch) aren't being swallowed upstream
- Keep devMode enabled in development so the block is surfaced where you render
When it happens
Trigger: Any uncaught exception raised while parsing or executing a template — e.g. a template expression referencing missing variables, a processor throwing (like ka:dispatch or th:each validation errors), or I/O failures — propagates through this wrapper in Markup.process.
Common situations: Dev-mode rendering of a page whose template has a runtime error; seeing this wrapper's message in an HTTP response body during development; diagnosing why a page failed by reading the embedded template source excerpt.
Related errors
- {diagnostic: frame switch failed with child-frame url list}
- Token exchange failed: " + e.getMessage()
- karate-markup does not support param lists in th:fragment…
- cannot find resource
- ka:data requires format 'varName:expression', got
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/9aa9f5356528f57a.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/markup/Markup.java:119
TemplateManager templateManager = wrapped.getConfiguration().getTemplateManager();
templateManager.parseAndProcess(templateSpec, context, writer);
try {
writer.flush();
} catch (IOException e) {
throw new TemplateOutputException("error flushing output writer", content, -1, -1, e);
}
} catch (ResourceNotFoundException e) {
throw e; // Let 404s bubble up without logging
} catch (Exception e) {
if (hasFlowControlSignal(e)) {
// intentional control flow (e.g. context.redirect, context.switch) — not an error
throw new RuntimeException(e);
}
String formatted = logTemplateError(isPath, content, e);
// Carry the formatted block as the wrapper's message so callers
// (e.g. ServerRequestCycle.handleError) can surface it in the
// response body when in devMode without re-deriving line/col/source.
throw new RuntimeException(formatted, e);
}
}
private static boolean hasFlowControlSignal(Throwable e) {
Throwable t = e;
while (t != null) {
if (t instanceof FlowControlSignal) {
return true;
}
t = t.getCause();
}
return false;
}
private static final int CONTEXT_LINES = 2;
private String logTemplateError(boolean isPath, String template, Exception e) {
StringBuilder sb = new StringBuilder();View on GitHub (pinned to a22eb90246)