{"record":{"id":"c6776b8f5dd863ee","repo":"apache/shenyu","slug":"http-status-error-from-backend-response-dynamic-status-from","errorCode":null,"errorMessage":"HTTP status error from backend response (dynamic status from shenyuResponse)","messagePattern":"HTTP status error from backend response \\(dynamic status from shenyuResponse\\)","errorType":"http","errorClass":"HttpClientErrorException","httpStatus":null,"severity":"error","filePath":"shenyu-sdk/shenyu-sdk-spring/src/main/java/org/apache/shenyu/sdk/spring/proxy/ShenyuClientMethodHandler.java","lineNumber":84,"sourceCode":"     *\n     * @param args args\n     * @return {@link Object}\n     * @throws IOException err\n     */\n    public Object invoke(final Object[] args) throws IOException {\n        final ShenyuRequest shenyuRequest = targetProcessor(requestTemplate, args);\n        final ShenyuResponse shenyuResponse = shenyuHttpClient.execute(shenyuRequest);\n        return handlerResponse(shenyuResponse, shenyuRequest.getRequestTemplate().getReturnType());\n    }\n\n    private Object handlerResponse(final ShenyuResponse shenyuResponse, final Class<?> returnType) {\n        if (Objects.isNull(shenyuResponse) || void.class == returnType) {\n            return null;\n        } else if (ShenyuResponse.class == returnType) {\n            return shenyuResponse;\n        } else if (shenyuResponse.getStatus() != HttpStatus.OK.value()) {\n            log.warn(\"handlerResponse http status warn shenyuResponse {}\", JsonUtils.toJson(shenyuResponse));\n            throw new HttpClientErrorException(HttpStatus.valueOf(shenyuResponse.getStatus()));\n        } else if (StringUtils.hasText(shenyuResponse.getBody())) {\n            return JsonUtils.jsonToObject(shenyuResponse.getBody(), returnType);\n        } else {\n            return null;\n        }\n    }\n\n    private ShenyuRequest targetProcessor(final RequestTemplate requestTemplate, final Object[] args) {\n        final RequestTemplate requestTemplateFrom = RequestTemplate.from(requestTemplate);\n        ShenyuRequest request = requestTemplateFrom.request();\n        for (RequestTemplate.ParamMetadata paramMetadata : requestTemplateFrom.getParamMetadataList()) {\n            final Annotation[] paramAnnotations = paramMetadata.getParamAnnotations();\n            for (Annotation paramAnnotation : paramAnnotations) {\n                final AnnotatedParameterProcessor processor = annotatedArgumentProcessors.get(paramAnnotation.annotationType());\n                if (ObjectUtils.isEmpty(processor)) {\n                    continue;\n                }\n                processor.processArgument(request, paramAnnotation, args[paramMetadata.getParamIndexOnMethod()]);","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/apache/shenyu/blob/567142e07261b3e615ae8850b30f4421f455cc5d/shenyu-sdk/shenyu-sdk-spring/src/main/java/org/apache/shenyu/sdk/spring/proxy/ShenyuClientMethodHandler.java#L66-L102","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read HttpClientErrorException.getStatusCode() and getResponseBodyAsString() to identify the exact upstream failure.","Check that the annotation path/verb matches the route configured in the ShenYu admin for this service.","If the backend is failing, fix or restart it; check gateway logs for the forwarded request.","Declare the return type as ShenyuResponse when you want to inspect non-200 statuses yourself instead of throwing."],"exampleFix":"// before\n@Get(\"/order/detail\")\nOrderDTO getById(Long id); // throws on 404\n// after (inspect status manually)\n@Get(\"/order/detail\")\nShenyuResponse getById(Long id);\nif (response.getStatus() == 404) { /* handle */ }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    return orderApi.getById(id);\n} catch (HttpClientErrorException e) {\n    switch (e.getStatusCode().value()) {\n        case 404: return null;\n        case 401: case 403: throw new AuthException(e);\n        default: throw new BackendException(e.getResponseBodyAsString(), e);\n    }\n}","preventionTips":["Keep annotation paths/verbs in sync with ShenYu admin route configuration.","Catch HttpClientErrorException and branch on status instead of letting it bubble.","Use ShenyuResponse return type when you need to inspect non-200 responses.","Verify backend health before blaming the gateway; check gateway logs for forwarded errors."],"tags":["http","remote-call","error-handling"],"backgroundTag":"http-error-response","analyzedSha":"567142e07261b3e615ae8850b30f4421f455cc5d","analyzedAt":"2026-09-12T10:08:21.293Z","contentChangedAt":"2026-09-12T10:08:21.293Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}