apache/shenyu · error · HttpClientErrorException

HTTP status error from backend response (dynamic status…

Error message

HTTP status error from backend response (dynamic status from shenyuResponse)

What it means

handlerResponse() maps the gateway's ShenyuResponse to the declared method return type. If the returned status is not 200 it logs the response and throws HttpClientErrorException built from that dynamic status. The message text is generic; the HTTP status code and body on the exception carry the real detail.

Solutions

  1. Read HttpClientErrorException.getStatusCode() and getResponseBodyAsString() to identify the exact upstream failure.
  2. Check that the annotation path/verb matches the route configured in the ShenYu admin for this service.
  3. If the backend is failing, fix or restart it; check gateway logs for the forwarded request.
  4. Declare the return type as ShenyuResponse when you want to inspect non-200 statuses yourself instead of throwing.

Example fix

// before
@Get("/order/detail")
OrderDTO getById(Long id); // throws on 404
// after (inspect status manually)
@Get("/order/detail")
ShenyuResponse getById(Long id);
if (response.getStatus() == 404) { /* handle */ }
Defensive patterns

Strategy: try-catch

Try / catch

try {
    return orderApi.getById(id);
} catch (HttpClientErrorException e) {
    switch (e.getStatusCode().value()) {
        case 404: return null;
        case 401: case 403: throw new AuthException(e);
        default: throw new BackendException(e.getResponseBodyAsString(), e);
    }
}

Prevention

When it happens

Trigger: The gateway/backend returned a non-200 status on a @ShenyuClient call whose return type is not ShenyuResponse/void — 404 for a wrong path, 500 from the backend, 401/403 from auth filters.

Common situations: Path or HTTP verb mismatch between interface annotation and gateway route; backend service down behind the gateway; authentication plugin rejecting the request; payload failing backend validation.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12). Data as JSON: /api/errors/c6776b8f5dd863ee. Report an issue: GitHub.

Appendix: source

Thrown at shenyu-sdk/shenyu-sdk-spring/src/main/java/org/apache/shenyu/sdk/spring/proxy/ShenyuClientMethodHandler.java:84

     *
     * @param args args
     * @return {@link Object}
     * @throws IOException err
     */
    public Object invoke(final Object[] args) throws IOException {
        final ShenyuRequest shenyuRequest = targetProcessor(requestTemplate, args);
        final ShenyuResponse shenyuResponse = shenyuHttpClient.execute(shenyuRequest);
        return handlerResponse(shenyuResponse, shenyuRequest.getRequestTemplate().getReturnType());
    }

    private Object handlerResponse(final ShenyuResponse shenyuResponse, final Class<?> returnType) {
        if (Objects.isNull(shenyuResponse) || void.class == returnType) {
            return null;
        } else if (ShenyuResponse.class == returnType) {
            return shenyuResponse;
        } else if (shenyuResponse.getStatus() != HttpStatus.OK.value()) {
            log.warn("handlerResponse http status warn shenyuResponse {}", JsonUtils.toJson(shenyuResponse));
            throw new HttpClientErrorException(HttpStatus.valueOf(shenyuResponse.getStatus()));
        } else if (StringUtils.hasText(shenyuResponse.getBody())) {
            return JsonUtils.jsonToObject(shenyuResponse.getBody(), returnType);
        } else {
            return null;
        }
    }

    private ShenyuRequest targetProcessor(final RequestTemplate requestTemplate, final Object[] args) {
        final RequestTemplate requestTemplateFrom = RequestTemplate.from(requestTemplate);
        ShenyuRequest request = requestTemplateFrom.request();
        for (RequestTemplate.ParamMetadata paramMetadata : requestTemplateFrom.getParamMetadataList()) {
            final Annotation[] paramAnnotations = paramMetadata.getParamAnnotations();
            for (Annotation paramAnnotation : paramAnnotations) {
                final AnnotatedParameterProcessor processor = annotatedArgumentProcessors.get(paramAnnotation.annotationType());
                if (ObjectUtils.isEmpty(processor)) {
                    continue;
                }
                processor.processArgument(request, paramAnnotation, args[paramMetadata.getParamIndexOnMethod()]);

View on GitHub (pinned to 567142e072)