question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Custom errors raised by resolvers are not formatted

See original GitHub issue

I’m attempting to mimic a couple of the errors in apollo-server, and want to include a code value in the extensions, as such:

class AuthenticationError(Exception):
    @property
    def formatted(self) -> dict:
        return {"message": self.message, "extensions": {"code": "UNAUTHENTICATED",}}

def throwError(_, info: ResolveInfo):
    raise AuthenticationError("dummy auth error")

After much stack debugging, I discovered that python’s graphql.error.located_error.located_error() is wrapping my custom child class in a new instance of GraphQLError, so its formatted property is never accessed by ariadne.

According to the ariadne error-messaging documentation, the default formatter “Unwraps GraphQL error by accessing its original_error property”. And while I realize that in the code this is happening exactly in the order described in the docs, this also means that the formatted = error.formatted is being called on the result of located_error() rather than the original one that was thrown.

Additionally, when I hook the debugger into format_error, it shows that my original error is actually double-wrapped, and only accessible by drilling two layers deep via error.original_error.original_error (the first level error.original_error is another GraphQLError … not quite sure where the double-wrapping got added)

It feels like there is a bug in here somewhere (especially with that nested original_error), but it’s certainly the case where the documentation could use some work explaining what actually happens under the hood, since as written, it sounds like it should be possible to just add a formatted property on an error and expect it to be reported to the API (which is definitely not the case).

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:12 (8 by maintainers)

github_iconTop GitHub Comments

2reactions
rafalpcommented, Mar 26, 2020

I’ve tried reproducing Apollo’s errors approach in past and I don’t remember it ever being nearly as troublesome as you are describing it, eg. I’ve never had to touch formatter logic in order to get those errors to work.

So I’ve did a quick test and it turns out I was wrong: you aren’t supposed to extend GraphQLError in your exception, extending regular Exception does the trick just fine:

from ariadne import QueryType, make_executable_schema
from ariadne.asgi import GraphQL


class AuthenticationError(Exception):
    extensions = {"code": "UNAUTHENTICATED"}


type_def = """
    type Query {
        field: Boolean
    }
"""

query_type = QueryType()


@query_type.field("field")
def counter_resolver(*_):
    raise AuthenticationError("PLEASE LOGIN")


schema = make_executable_schema(type_def, query_type)
app = GraphQL(schema, debug=True)

GraphQL response:

{
  "data": {
    "field": null
  },
  "errors": [
    {
      "message": "PLEASE LOGIN",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "field"
      ],
      "extensions": {
        "code": "UNAUTHENTICATED",
        "exception": {
          "stacktrace": [
            "Traceback (most recent call last):",
            "  File \"/Users/rafalpiton/github/ariadne/venv/lib/python3.7/site-packages/graphql/execution/execute.py\", line 625, in resolve_field_value_or_error",
            "    result = resolve_fn(source, info, **args)",
            "  File \"./errortest.py\", line 21, in counter_resolver",
            "    raise AuthenticationError(\"PLEASE LOGIN\")",
            "errortest.AuthenticationError: PLEASE LOGIN"
          ],
          "context": {
            "_": "(None, GraphQLResolv...0x105cee080>}))"
          }
        }
      }
    }
  ]
}

And same query with DEBUG=False:

{
  "data": {
    "field": null
  },
  "errors": [
    {
      "message": "PLEASE LOGIN",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "field"
      ],
      "extensions": {
        "code": "UNAUTHENTICATED"
      }
    }
  ]
}

You also don’t need to write any custom formatting logic for this because when query executor wraps exception in GraphQLError, it does graphql_error.extensions = original_error.extensions, effectively passing error code data up to error formatter.

1reaction
rafalpcommented, Sep 29, 2021

@nilansaha this is not related to this issue. Please ask question on discussions instead.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Error messaging — Ariadne 0.3 documentation
Your first instinct when planning error messaging may be to use this approach to communicate custom errors (like permission or validation errors) raised...
Read more >
Full Stack Error Handling with GraphQL and Apollo
On its own, the GraphQL spec itself provides little guidance on how to format error responses, requiring only a message field with a...
Read more >
GraphQL java send custom error in json format
GraphQL specification defines a clear format for the error entry in the response. According to the spec, it should like this (assuming JSON ......
Read more >
Handling GraphQL errors like a champ with unions and ...
No type error will be thrown if you mistype the error message or extension code inside your resolvers. The GraphQL engine does not...
Read more >
Error Handling in GraphQL With Spring Boot
These resolvers are sequentially invoked until one of them is able to handle the exception and resolve it to a GraphQLError. If no...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found