karatelabs/karate · error · TemplateOutputException
error flushing output writer
Error message
error flushing output writer
What it means
After Thymeleaf finishes parsing and processing a template into a writer, Markup.process flushes that writer. If flush() throws an IOException, it is wrapped in a TemplateOutputException with the message "error flushing output writer" and the template content as context. This indicates the underlying output sink (a backing file, socket, or response stream) failed while being flushed, not a problem with the template itself.
Solutions
- Check the cause (`TemplateOutputException.getCause()` IOException) to identify the actual failing stream.
- For HTTP responses, guard against writing after the response is committed or the client has disconnected before invoking Markup.process.
- Retry the render if the failure was transient (e.g. a reset connection); for persistent stream errors, fix the writer lifecycle so it is open and writable.
- Verify disk space and file permissions if the writer is file-backed.
Defensive patterns
Strategy: try-catch
Validate before calling
// Before rendering, ensure the target writer is open and writable
if (!response.isCommitted() && writer != null) {
render(template, writer);
} Try / catch
try {
markup.process(...);
} catch (TemplateOutputException e) {
if ("error flushing output writer".equals(e.getMessage()) && e.getCause() instanceof IOException io) {
log.warn("Stream flush failed (client gone? disk full?): {}", io.getMessage());
return; // often safe to swallow for aborted client connections
}
throw e;
} Prevention
- Don't render into a response stream that is already committed or closed
- Handle client-disconnect IOExceptions as benign for HTML responses
- Monitor disk space for file-backed writers
- Keep the template-independent cause chain for diagnosis
When it happens
Trigger: Calling Markup.process(...) where the TemplateManager writes to a writer backed by a stream that fails on flush — e.g. a closed response stream, a full/broken pipe to a client, or a disk-backed writer that hit an I/O failure.
Common situations: Client disconnected mid-response when rendering HTML for an HTTP response; servlet output stream already committed/closed before flush; container shutdown or connection reset during template rendering; underlying file system errors for file-backed writers.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- Failed to read bytes from
- Failed to open stream for:
- ext ' ': resource vanished after validation
- ext ' ': failed reading
- Failed to create session directory: " + directory
AI-assisted analysis of karatelabs/karate@a22eb90246 (2026-09-12).
Data as JSON: /api/errors/9e695547febd39e1.
Report an issue: GitHub.
Appendix: source
Thrown at karate-core/src/main/java/io/karatelabs/markup/Markup.java:106
Writer stringWriter = new FastStringWriter(100);
process(isPath, content, context, stringWriter);
return stringWriter.toString();
}
private void process(boolean isPath, String content, IContext context, Writer writer) {
try {
// Reset resolver state so processString can be called multiple times
if (templateResolver != null) {
templateResolver.resetStringTemplateState();
}
// the empty map (which becomes null) is used to signal an inline string template for HtmlTemplateResolver to handle
TemplateSpec templateSpec = new TemplateSpec(content, isPath ? Collections.emptyMap() : IS_STRING);
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;View on GitHub (pinned to a22eb90246)