Netflix/zuul · error · ZuulException
Received invalid message from origin
Error message
Received invalid message from origin
What it means
ClientResponseWriter.channelRead receives messages coming back from the origin through the client-facing pipeline and only recognizes valid Zuul HTTP response messages (HttpResponse start plus HttpContent chunks). Any other message type from the origin side is released and this ZuulException is thrown, because Zuul cannot safely forward it to the client. Like the sibling checks, it is an internal invariant guard labeled 'should never happen'.
Solutions
- Identify which component injected the unexpected message by adding a logging handler on the response pipeline to print msg.getClass().
- Fix the custom origin filter/handler to emit proper Zuul/Netty HTTP response types (HttpResponse, HttpContent).
- Review recent pipeline customizations (ClientRequestReceiver/Netty channel init) for handlers inserted between the origin response receiver and the client writer.
- Pin/align zuul-core and custom filter dependencies to a consistent version so response message types match expectations.
Example fix
// before (custom origin handler) ctx.fireChannelRead(myCustomObject); // after ctx.fireChannelRead(new DefaultLastHttpContent()); // proper Netty/Zuul response content types only
Defensive patterns
Strategy: validation
Validate before calling
// before forwarding origin messages downstream, ensure they are valid HTTP response types
if (!(msg instanceof HttpResponse || msg instanceof HttpContent)) {
throw new IllegalArgumentException("Unsupported origin message: " + msg.getClass().getName());
} Type guard
boolean isValidOriginMessage(Object msg) {
return msg instanceof HttpResponse || msg instanceof HttpContent;
} Try / catch
try {
// pipeline read path
} catch (ZuulException e) {
if ("Received invalid message from origin".equals(e.getMessage())) {
log.error("Origin pipeline emitted non-HTTP message", e);
channel.close();
}
} Prevention
- Custom origin-side handlers must only fire HttpResponse/HttpContent messages
- Test custom pipelines with a wire-logging handler to spot foreign message types
- Keep zuul-core and Netty versions consistent across custom handlers
- Avoid inserting handlers between origin receiver and client writer that emit custom objects
When it happens
Trigger: A message that is neither an expected origin response type (e.g. not an HttpResponse/HttpContent/ZuulMessage instance) arrives at ClientResponseWriter.channelRead — caused by a custom origin filter/handler injecting a foreign object, or a pipeline wiring change passing unexpected messages downstream.
Common situations: Custom origin-side filters returning raw objects; in-flight Netty or zuul-core upgrades changing message types; misconfigured channel pipelines where a handler between origin and client emits its own message types.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
AI-assisted analysis of Netflix/zuul@14bf53c52d (2026-09-07).
Data as JSON: /api/errors/7bb917364bf4e389.
Report an issue: GitHub.
Appendix: source
Thrown at zuul-core/src/main/java/com/netflix/zuul/netty/server/ClientResponseWriter.java:150
channel.write(buildHttpResponse(zuulResponse));
writeBufferedBodyContent(zuulResponse, channel);
channel.flush();
} else {
resp.disposeBufferedBody();
channel.close();
}
} else if (msg instanceof HttpContent chunk) {
if (channel.isActive()) {
channel.writeAndFlush(chunk);
} else {
chunk.release();
channel.close();
}
} else {
// should never happen
ReferenceCountUtil.release(msg);
throw new ZuulException("Received invalid message from origin", true);
}
}
protected boolean shouldAllowPreemptiveResponse(Channel channel) {
// If the request timed-out while being read, then there won't have been any LastContent, but that's ok because
// the connection will have to be discarded anyway.
StatusCategory status =
StatusCategoryUtils.getStatusCategory(ClientRequestReceiver.getRequestFromChannel(channel));
return status == ZuulStatusCategory.FAILURE_CLIENT_TIMEOUT;
}
protected boolean skipProcessing(HttpResponseMessage resp) {
// override if you need to skip processing of response
return false;
}
protected void writeBufferedBodyContent(HttpResponseMessage zuulResponse, Channel channel) {
zuulResponse.getBodyContents().forEach(chunk -> channel.write(chunk.retain()));View on GitHub (pinned to 14bf53c52d)