{"record":{"id":"9aa9f5356528f57a","repo":"karatelabs/karate","slug":"formatted-template-error-block-template-source-line-col","errorCode":null,"errorMessage":"${formatted template error block (template source, line/col, cause)}","messagePattern":"\\$\\{formatted template error block \\(template source, line/col, cause\\)\\}","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"karate-core/src/main/java/io/karatelabs/markup/Markup.java","lineNumber":119,"sourceCode":"            TemplateManager templateManager = wrapped.getConfiguration().getTemplateManager();\n            templateManager.parseAndProcess(templateSpec, context, writer);\n            try {\n                writer.flush();\n            } catch (IOException e) {\n                throw new TemplateOutputException(\"error flushing output writer\", content, -1, -1, e);\n            }\n        } catch (ResourceNotFoundException e) {\n            throw e; // Let 404s bubble up without logging\n        } catch (Exception e) {\n            if (hasFlowControlSignal(e)) {\n                // intentional control flow (e.g. context.redirect, context.switch) — not an error\n                throw new RuntimeException(e);\n            }\n            String formatted = logTemplateError(isPath, content, e);\n            // Carry the formatted block as the wrapper's message so callers\n            // (e.g. ServerRequestCycle.handleError) can surface it in the\n            // response body when in devMode without re-deriving line/col/source.\n            throw new RuntimeException(formatted, e);\n        }\n    }\n\n    private static boolean hasFlowControlSignal(Throwable e) {\n        Throwable t = e;\n        while (t != null) {\n            if (t instanceof FlowControlSignal) {\n                return true;\n            }\n            t = t.getCause();\n        }\n        return false;\n    }\n\n    private static final int CONTEXT_LINES = 2;\n\n    private String logTemplateError(boolean isPath, String template, Exception e) {\n        StringBuilder sb = new StringBuilder();","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/karatelabs/karate/blob/a22eb90246d958d15a47bf436693d0121ad2812d/karate-core/src/main/java/io/karatelabs/markup/Markup.java#L101-L137","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (generic handling that hides the formatted block)\n} catch (Exception e) {\n    log.error(\"render failed\");\n}\n\n// after (surface the formatted block and root cause)\n} catch (RuntimeException e) {\n    log.error(\"template render failed:\\n{}\\nroot cause:\", e.getMessage(), e.getCause(), e);\n    throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    markup.process(...);\n} catch (RuntimeException e) {\n    // e.getMessage() is the formatted template error block; e is the root cause\n    log.error(\"Template render failed:\\n{}\", e.getMessage(), e);\n    if (!devMode) {\n        throw new IllegalArgumentException(\"template render failed (see logs)\", e);\n    }\n    throw e; // devMode callers can print the formatted block to the response\n}","preventionTips":["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"],"tags":["template","error-handling","diagnostics","wrapper"],"backgroundTag":"unexpected-response-shape","analyzedSha":"a22eb90246d958d15a47bf436693d0121ad2812d","analyzedAt":"2026-09-12T09:01:00.220Z","contentChangedAt":"2026-09-12T09:01:00.220Z","schemaVersion":2},"datasetVersion":"2026-09-16T19:17:19.609Z"}