{"id":20660917,"url":"https://github.com/openfeign/feign-annotation-error-decoder","last_synced_at":"2026-04-02T18:43:51.582Z","repository":{"id":48593016,"uuid":"90207971","full_name":"OpenFeign/feign-annotation-error-decoder","owner":"OpenFeign","description":null,"archived":false,"fork":false,"pushed_at":"2020-09-24T10:39:04.000Z","size":149,"stargazers_count":51,"open_issues_count":10,"forks_count":9,"subscribers_count":3,"default_branch":"master","last_synced_at":"2026-03-15T16:53:25.935Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OpenFeign.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-04T01:20:48.000Z","updated_at":"2023-12-15T19:25:39.000Z","dependencies_parsed_at":"2022-09-03T05:20:38.326Z","dependency_job_id":null,"html_url":"https://github.com/OpenFeign/feign-annotation-error-decoder","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/OpenFeign/feign-annotation-error-decoder","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenFeign%2Ffeign-annotation-error-decoder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenFeign%2Ffeign-annotation-error-decoder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenFeign%2Ffeign-annotation-error-decoder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenFeign%2Ffeign-annotation-error-decoder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OpenFeign","download_url":"https://codeload.github.com/OpenFeign/feign-annotation-error-decoder/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenFeign%2Ffeign-annotation-error-decoder/sbom","scorecard":{"id":105256,"data":{"date":"2025-08-11","repo":{"name":"github.com/OpenFeign/feign-annotation-error-decoder","commit":"546752425238f3d42d0ceec91d5c5af05841803e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.2,"checks":[{"name":"Code-Review","score":2,"reason":"Found 4/19 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":9,"reason":"binaries present in source code","details":["Warn: binary detected: .mvn/wrapper/maven-wrapper.jar:1"],"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 15 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-15T10:58:43.660Z","repository_id":48593016,"created_at":"2025-08-15T10:58:43.660Z","updated_at":"2025-08-15T10:58:43.660Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31313235,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-16T19:06:30.645Z","updated_at":"2026-04-02T18:43:51.566Z","avatar_url":"https://github.com/OpenFeign.png","language":"Java","readme":"Annotation Error Decoder\n=========================\n[![Join the chat at https://gitter.im/OpenFeign/feign](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/OpenFeign/feign?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![Build Status](https://travis-ci.org/OpenFeign/feign-annotation-error-decoder.svg?branch=master)](https://travis-ci.org/OpenFeign/feign-annotation-error-decoder)\n[![Download](https://api.bintray.com/packages/openfeign/maven/feign-annotation-error-decoder/images/download.svg) ](https://bintray.com/openfeign/maven/feign-annotation-error-decoder/_latestVersion)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.github.openfeign/feign-annotation-error-decoder/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.github.openfeign/feign-annotation-error-decoder)\n\nThis module allows to annotate Feign's interfaces with annotations to generate Exceptions based on error codes\n\nTo use AnnotationErrorDecoder with Feign, add the Annotation Error Decoder module to your classpath. Then, configure\nFeign to use the AnnotationErrorDecoder:\n\n```java\nGitHub github = Feign.builder()\n                     .errorDecoder(\n                         AnnotationErrorDecoder.builderFor(GitHub.class).build()\n                     )\n                     .target(GitHub.class, \"https://api.github.com\");\n```\n\n## Leveraging the annotations and priority order\nFor annotation decoding to work, the class must be annotated with `@ErrorHandling` tags.\nThe tags are valid in both the class level as well as method level. They will be treated from 'most specific' to \n'least specific' in the following order:\n* A code specific exception defined on the method \n* A code specific exception defined on the class\n* The default exception of the method\n* The default exception of the class\n\n```java\n@ErrorHandling(codeSpecific =\n    {\n        @ErrorCodes( codes = {401}, generate = UnAuthorizedException.class),\n        @ErrorCodes( codes = {403}, generate = ForbiddenException.class),\n        @ErrorCodes( codes = {404}, generate = UnknownItemException.class),\n    },\n    defaultException = ClassLevelDefaultException.class\n)\ninterface GitHub {\n\n    @ErrorHandling(codeSpecific =\n        {\n            @ErrorCodes( codes = {404}, generate = NonExistentRepoException.class),\n            @ErrorCodes( codes = {502, 503, 504}, generate = RetryAfterCertainTimeException.class),\n        },\n        defaultException = FailedToGetContributorsException.class\n    )\n    @RequestLine(\"GET /repos/{owner}/{repo}/contributors\")\n    List\u003cContributor\u003e contributors(@Param(\"owner\") String owner, @Param(\"repo\") String repo);\n}\n```\nIn the above example, error responses to 'contributors' would hence be mapped as follows by status codes:\n\n| Code        | Exception                          | Reason                |\n| ----------- | --------------------------------   | --------------------- |\n| 401         | `UnAuthorizedException`            | from Class definition |\n| 403         | `ForbiddenException`               | from Class definition |\n| 404         | `NonExistenRepoException`          | from Method definition, note that the class generic exception won't be thrown here |\n| 502,503,504 | `RetryAfterCertainTimeException`   | from method definition. Note that you can have multiple error codes generate the same type of exception |\n| Any Other   | `FailedToGetContributorsException` | from Method default   |\n\nFor a class level default exception to be thrown, the method must not have a `defaultException` defined, nor must the error code\nbe mapped at either the method or class level.\n\nIf the return code cannot be mapped to any code and no default exceptions have been configured, then the decoder will\ndrop to a default decoder (by default, the standard one provided by feign). You can change the default drop-into decoder\nas follows:\n\n```java\nGitHub github = Feign.builder()\n                     .errorDecoder(\n                         AnnotationErrorDecoder.builderFor(GitHub.class)\n                            .withDefaultDecoder(new MyOtherErrorDecoder())\n                            .build()\n                     )\n                     .target(GitHub.class, \"https://api.github.com\");\n```\n\n \n## Complex Exceptions\n\nAny exception can be used if they have a default constructor:\n\n```java\nclass DefaultConstructorException extends Exception {}\n```\n\nHowever, if you want to have parameters (such  as the feign.Request object or response body or response headers), you have to annotate its \nconstructor appropriately (the body annotation is optional, provided there aren't paramters which will clash)\n\nAll the following examples are valid exceptions:\n```java\nclass JustBody extends Exception {\n\n    @FeignExceptionConstructor\n    public JustBody(String body) {\n\n    }\n}\nclass JustRequest extends Exception {\n\n    @FeignExceptionConstructor\n    public JustRequest(Request request) {\n\n    }\n}\nclass RequestAndResponseBody extends Exception {\n\n    @FeignExceptionConstructor\n    public RequestAndResponseBody(Request request, String body) {\n\n    }\n}\n//Headers must be of type Map\u003cString, Collection\u003cString\u003e\u003e\nclass BodyAndHeaders extends Exception {\n\n    @FeignExceptionConstructor\n    public BodyAndHeaders(@ResponseBody String body, @ResponseHeaders Map\u003cString, Collection\u003cString\u003e\u003e headers) {\n\n    }\n}\nclass RequestAndResponseBodyAndHeaders extends Exception {\n\n    @FeignExceptionConstructor\n    public RequestAndResponseBodyAndHeaders(Request request, @ResponseBody String body, @ResponseHeaders Map\u003cString, Collection\u003cString\u003e\u003e headers) {\n\n    }\n}\nclass JustHeaders extends Exception {\n\n    @FeignExceptionConstructor\n    public JustHeaders(@ResponseHeaders Map\u003cString, Collection\u003cString\u003e\u003e headers) {\n\n    }\n}\n```\n\nIf you want to have the body decoded, you'll need to pass a decoder at construction time (just as for normal responses):\n\n```java\nGitHub github = Feign.builder()\n                     .errorDecoder(\n                         AnnotationErrorDecoder.builderFor(GitHub.class)\n                            .withResponseBodyDecoder(new JacksonDecoder())\n                            .build()\n                     )\n                     .target(GitHub.class, \"https://api.github.com\");\n```\n\nThis will enable you to create exceptions where the body is a complex pojo:\n\n```java\nclass ComplexPojoException extends Exception {\n\n    @FeignExceptionConstructor\n    public ComplexPojoException(GithubExceptionResponse body) {\n        if (body != null) {\n            // extract data\n        } else {\n            // fallback code\n        }\n    }\n}\n//The pojo can then be anything you'd like provided the decoder can manage it\nclass GithubExceptionResponse {\n    public String message;\n    public int githubCode;\n    public List\u003cString\u003e urlsForHelp;\n}\n```\n\nIt's worth noting that at setup/startup time, the generators are checked with a null value of the body.\nIf you don't do the null-checker, you'll get an NPE and startup will fail.\n\n\n## Inheriting from other interface definitions\nYou can create a client interface that inherits from a different one. However, there are some limitations that\nyou should be aware of (for most cases, these shouldn't be an issue):\n* The inheritance is not natural java inheritance of annotations - as these don't work on interfaces\n* Instead, the error looks at the class and if it finds the `@ErrorHandling` annotation, it uses that one.\n* If not, it will look at *all* the interfaces the main interface `extends` - but it does so in the order the\njava API gives it - so order is not guaranteed.\n* If it finds the annotation in one of those parents, it uses that definition, without looking at any other\n* That means that if more than one interface was extended which contained the `@ErrorHandling` annotation, we can't\nreally guarantee which one of the parents will be selected and you should really do handling at the child interface\n  * so far, the java API seems to return in order of definition after the `extends`, but it's a really bad practice\n  if you have to depend on that... so our suggestion: don't.\n\nThat means that as long as you only ever extend from a base interface (where you may decide that all 404's are \"NotFoundException\", for example)\nthen you should be ok. But if you get complex in polymorphism, all bets are off - so don't go crazy!\n\nExample:\nIn the following code:\n* The base `FeignClientBase` interface defines a default set of exceptions at class level\n* the `GitHub1` and `GitHub2` interfaces will inherit the class-level error handling, which means that\nany 401/403/404 will be handled correctly (provided the method doesn't specify a more specific exception)\n* the `GitHub3` interface however, by defining its own error handling, will handle all 401's, but not the\n403/404's since there's no merging/etc (not really in the plan to implement either...)\n```java\n\n@ErrorHandling(codeSpecific =\n    {\n        @ErrorCodes( codes = {401}, generate = UnAuthorizedException.class),\n        @ErrorCodes( codes = {403}, generate = ForbiddenException.class),\n        @ErrorCodes( codes = {404}, generate = UnknownItemException.class),\n    },\n    defaultException = ClassLevelDefaultException.class\n)\ninterface FeignClientBase {}\n\ninterface GitHub1 extends FeignClientBase {\n\n    @ErrorHandling(codeSpecific =\n        {\n            @ErrorCodes( codes = {404}, generate = NonExistentRepoException.class),\n            @ErrorCodes( codes = {502, 503, 504}, generate = RetryAfterCertainTimeException.class),\n        },\n        defaultException = FailedToGetContributorsException.class\n    )\n    @RequestLine(\"GET /repos/{owner}/{repo}/contributors\")\n    List\u003cContributor\u003e contributors(@Param(\"owner\") String owner, @Param(\"repo\") String repo);\n}\n\ninterface GitHub2 extends FeignClientBase {\n\n    @ErrorHandling(codeSpecific =\n        {\n            @ErrorCodes( codes = {404}, generate = NonExistentRepoException.class),\n            @ErrorCodes( codes = {502, 503, 504}, generate = RetryAfterCertainTimeException.class),\n        },\n        defaultException = FailedToGetContributorsException.class\n    )\n    @RequestLine(\"GET /repos/{owner}/{repo}/contributors\")\n    List\u003cContributor\u003e contributors(@Param(\"owner\") String owner, @Param(\"repo\") String repo);\n}\n\n@ErrorHandling(codeSpecific =\n    {\n        @ErrorCodes( codes = {401}, generate = UnAuthorizedException.class)\n    },\n    defaultException = ClassLevelDefaultException.class\n)\ninterface GitHub3 extends FeignClientBase {\n\n    @ErrorHandling(codeSpecific =\n        {\n            @ErrorCodes( codes = {404}, generate = NonExistentRepoException.class),\n            @ErrorCodes( codes = {502, 503, 504}, generate = RetryAfterCertainTimeException.class),\n        },\n        defaultException = FailedToGetContributorsException.class\n    )\n    @RequestLine(\"GET /repos/{owner}/{repo}/contributors\")\n    List\u003cContributor\u003e contributors(@Param(\"owner\") String owner, @Param(\"repo\") String repo);\n}\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenfeign%2Ffeign-annotation-error-decoder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenfeign%2Ffeign-annotation-error-decoder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenfeign%2Ffeign-annotation-error-decoder/lists"}