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

  1. Identify which component injected the unexpected message by adding a logging handler on the response pipeline to print msg.getClass().
  2. Fix the custom origin filter/handler to emit proper Zuul/Netty HTTP response types (HttpResponse, HttpContent).
  3. Review recent pipeline customizations (ClientRequestReceiver/Netty channel init) for handlers inserted between the origin response receiver and the client writer.
  4. 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

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)