{"record":{"id":"eb2c035a9c50dfa9","repo":"OpenFeign/feign","slug":"s-reading-s-s-response-body","errorCode":null,"errorMessage":"%s reading %s %s (response body)","messagePattern":"(.+?) reading (.+?) (.+?) \\(response body\\)","errorType":"exception","errorClass":"FeignException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/feign/InvocationContext.java","lineNumber":80,"sourceCode":"    return returnType;\n  }\n\n  public Response response() {\n    return response;\n  }\n\n  public Object proceed() throws Exception {\n    if (returnType == Response.class) {\n      return disconnectResponseBodyIfNeeded(response);\n    }\n\n    try {\n      final boolean shouldDecodeResponseBody =\n          (response.status() >= 200 && response.status() < 300)\n              || (response.status() == 404 && dismiss404 && !isVoidType(returnType));\n\n      if (!shouldDecodeResponseBody) {\n        throw decodeError(configKey, response);\n      }\n\n      if (isVoidType(returnType) && !decodeVoid) {\n        ensureClosed(response.body());\n        return kotlinUnitInstance(returnType);\n      }\n\n      Class<?> rawType = Types.getRawType(returnType);\n      if (TypedResponse.class.isAssignableFrom(rawType)) {\n        Type bodyType = Types.resolveLastTypeParameter(returnType, TypedResponse.class);\n        return TypedResponse.builder(response).body(decode(response, bodyType)).build();\n      }\n\n      return decode(response, returnType);\n    } finally {\n      if (closeAfterDecode) {\n        ensureClosed(response.body());\n      }","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/OpenFeign/feign/blob/e2a1e27560a1e68840c34f031afca88b36096e30/core/src/main/java/feign/InvocationContext.java#L62-L98","documentation":"In InvocationContext.proceed, when the response status should NOT have its body decoded (status outside 2xx, or 404 without dismiss404 on a non-void type), decodeError is invoked to produce the exception from the ErrorDecoder, whose message is formatted \"%s reading %s %s (response body)\". It signals the library refused to decode a body because the response was not a decodable success (or dismissible 404).","triggerScenarios":"Server returned 4xx/5xx (non-2xx) for a method call; server returned 404 while dismiss404 is false (or the method's return type is not void); a redirect/status the ErrorDecoder maps to an exception.","commonSituations":"Upstream API returning 500 during an outage; resource genuinely missing (404) on a typed endpoint; wrong base URL hitting an error page; rate limiting 429 responses.","solutions":["Read the FeignException's status and content from the ErrorDecoder to see the server's error payload.","Install a custom ErrorDecoder to map expected statuses to domain exceptions or defaults.","Enable dismiss404 (404 -> null) via options if 404 is an acceptable outcome for non-void methods.","Fix the client-side cause (bad id, wrong URL, expired credentials) indicated by the status code."],"exampleFix":"// before\nFeign.builder().target(UserApi.class, url);\n// after\nFeign.builder()\n    .dismiss404()\n    .errorDecoder(new ErrorDecoder() {\n      public Exception decode(String methodKey, Response response) {\n        if (response.status() == 404) return new UserNotFoundException(methodKey);\n        return new Default().decode(methodKey, response);\n      }\n    })\n    .target(UserApi.class, url);","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return api.getUser(id);\n} catch (FeignException e) {\n  switch (e.status()) {\n    case 404: return Optional.empty();\n    case 429: throw new RateLimitedException(e);\n    default: throw e;\n  }\n}","preventionTips":["Register a custom ErrorDecoder mapping expected statuses to domain exceptions","Use dismiss404 when 404 is a valid empty result for non-void methods","Log FeignException status and body for diagnosis","Treat non-2xx as expected API outcomes and handle them per endpoint"],"tags":["http","response","decode","error-decoder"],"backgroundTag":"http-error-response","analyzedSha":"e2a1e27560a1e68840c34f031afca88b36096e30","analyzedAt":"2026-09-10T12:37:37.238Z","contentChangedAt":"2026-09-10T12:37:37.238Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}