Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GraphQL::Backtrace::TracedError behavior #5222

Open
yn-misaki opened this issue Feb 5, 2025 · 2 comments
Open

GraphQL::Backtrace::TracedError behavior #5222

yn-misaki opened this issue Feb 5, 2025 · 2 comments

Comments

@yn-misaki
Copy link

Describe the bug

I noticed a behavioral change in GraphQL version 2.4.9. I would like to confirm if this is an intended change in specification or if it's a potential bug.

  • Previous Behavior (2.4.8): Exceptions were caught directly by rescue_from blocks as defined in the schema.
  • Current Behavior (2.4.9): Exceptions are now wrapped in GraphQL::Backtrace::TracedError, preventing rescue_from from catching them as expected.

Versions

graphql version: 2.4.9
graphql-c_parser version: 1.1.2

GraphQL schema

module Types
  class AddressType < GraphQL::Schema::Object
    def self.authorized?(object, context)
       raise ApplicationSchema::CustomException
    end

    field :id
  end
end

class ApplicationSchema < GraphQL::Schema
  query Types::QueryType
  use GraphQL::Dataloader
  use GraphQL::Backtrace

  class CutomException < StandardError; end
  rescue_from(CustomException) do |err|
    raise GraphQL::ExecutionError.new(err.message, extensions: { code: "FORBIDDEN" })
  end
end

GraphQL query

Example GraphQL query and response (if query execution is involved)

query {
    query {
    node(id: "AddressID") {
      ... on Address {
       id
      }
    }
  }
}
Click to view response
{"data"=>nil,
 "errors"=>
  [{"message"=>
     "Unhandled error during GraphQL execution:\n" +
     "\n" +
     "  ApplicationSchema::CutomException\n" +
     "    /app/app/graphql/types/address_type.rb:6:in `authorized?'\n" +
     "    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/schema/object.rb:70:in `block in authorized_new'\n" +
     "    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/trace.rb:63:in `authorized'\n" +
     "    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/platform_trace.rb:75:in `block in authorized'\n" +
     "    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/sentry_trace.rb:86:in `block in instrument_sentry_execution'\n" +
     "    /usr/local/bundle/gems/sentry-ruby-5.22.2/lib/sentry/hub.rb:108:in `with_child_span'\n" +
     "    /usr/local/bundle/gems/sentry-ruby-5.22.2/lib/sentry-ruby.rb:506:in `with_child_span'\n" +
     "    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/sentry_trace.rb:85:in `instrument_sentry_execution'\n" +
     "    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/sentry_trace.rb:53:in `platform_authorized'\n" +
     "    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/platform_trace.rb:74:in `authorized'\n" +
     "    ... and 19 more lines\n" +
     "\n" +
     "Use #cause to access the original exception (including #cause.backtrace).\n" +
     "\n" +
     "GraphQL Backtrace:\n" +
     "Loc  | Field      | Object | Arguments                                                            | Result\n" +
     "2:3  | Query.node | nil    | {:id=>\"QWRkcmVzc3xmYmJhMDUzZS0yZDA2LTRmN2ItOWZkMC05YmM0ZmZjY2NjZGI\"} | #<ApplicationSchema::CutomException: ApplicationSchema::CutomException>\n" +
     "1:1  | query      | nil    | {}                                                                   | {}\n" +
     "\n",
    "extensions"=>
     {"code"=>"INTERNAL_SERVER_ERROR",
      "exception"=>
       {"stacktrace"=>
         ["/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/schema.rb:1132:in `handle_or_reraise'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/query.rb:409:in `handle_or_reraise'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/schema/object.rb:74:in `rescue in block in authorized_new'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/schema/object.rb:69:in `block in authorized_new'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/trace.rb:63:in `authorized'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/platform_trace.rb:75:in `block in authorized'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/sentry_trace.rb:86:in `block in instrument_sentry_execution'",
          "/usr/local/bundle/gems/sentry-ruby-5.22.2/lib/sentry/hub.rb:108:in `with_child_span'",
          "/usr/local/bundle/gems/sentry-ruby-5.22.2/lib/sentry-ruby.rb:506:in `with_child_span'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/sentry_trace.rb:85:in `instrument_sentry_execution'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/sentry_trace.rb:53:in `platform_authorized'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/platform_trace.rb:74:in `authorized'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/schema/object.rb:68:in `authorized_new'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/schema/object.rb:47:in `wrap'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:605:in `continue_field'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:600:in `block in continue_field'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:818:in `after_lazy'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:583:in `continue_field'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:393:in `block (2 levels) in evaluate_selection_with_resolved_keyword_args'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:818:in `after_lazy'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:386:in `block in evaluate_selection_with_resolved_keyword_args'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:704:in `call_method_on_directives'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:363:in `evaluate_selection_with_resolved_keyword_args'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:341:in `block in evaluate_selection_with_args'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:818:in `after_lazy'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:288:in `evaluate_selection_with_args'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/runtime.rb:282:in `block in evaluate_selection'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/execution/interpreter/arguments_cache.rb:46:in `block in dataload_for'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/schema/member/has_arguments.rb:272:in `block (3 levels) in coerce_arguments'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/dataloader.rb:273:in `block in spawn_job_fiber'",
          "/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/dataloader.rb:245:in `block in spawn_fiber'"]}}}]}

Expected behavior

I would like to request that unhandled errors (not defined in rescue_from) should be wrapped in GraphQL::Backtrace::TracedError.

Actual behavior

Click to view exception backtrace
Unhandled error during GraphQL execution:

  Application::CutomException
    /app/app/graphql/types/address_type.rb:6:in `authorized?'
    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/schema/object.rb:70:in `block in authorized_new'
    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/trace.rb:63:in `authorized'
    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/platform_trace.rb:75:in `block in authorized'
    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/sentry_trace.rb:86:in `block in instrument_sentry_execution'
    /usr/local/bundle/gems/sentry-ruby-5.22.2/lib/sentry/hub.rb:108:in `with_child_span'
    /usr/local/bundle/gems/sentry-ruby-5.22.2/lib/sentry-ruby.rb:506:in `with_child_span'
    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/sentry_trace.rb:85:in `instrument_sentry_execution'
    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/sentry_trace.rb:53:in `platform_authorized'
    /usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/platform_trace.rb:74:in `authorized'
    ... and 19 more lines

Use #cause to access the original exception (including #cause.backtrace).

GraphQL Backtrace:
Loc  | Field      | Object | Arguments                                                            | Result
2:3  | Query.node | nil    | {:id=>"QWRkcmVzc3xmOTAwNTNlNi01NTQ0LTQ4MDgtYWQwMi1hYTQ2Njk2NzFiMTU"} | #<ApplicationSchema::CutomException: Application::CutomException>
1:1  | query      | nil    | {}                                                                   | {}

/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/schema.rb:1132:in `handle_or_reraise'
/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/query.rb:409:in `handle_or_reraise'
/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/schema/object.rb:74:in `rescue in block in authorized_new'
/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/schema/object.rb:69:in `block in authorized_new'
/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/trace.rb:63:in `authorized'
/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/platform_trace.rb:75:in `block in authorized'
/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/sentry_trace.rb:86:in `block in instrument_sentry_execution'
/usr/local/bundle/gems/sentry-ruby-5.22.2/lib/sentry/hub.rb:108:in `with_child_span'
/usr/local/bundle/gems/sentry-ruby-5.22.2/lib/sentry-ruby.rb:506:in `with_child_span'
/usr/local/bundle/gems/graphql-2.4.9/lib/graphql/tracing/sentry_trace.rb:85:in `instrument_sentry_execution'

Additional context

The behavior change appears to be related to the modifications in PR #5217.

Proposed Error Handling Flow

I would like to request the following error handling behavior:

  1. Search for errors in Execution::Errors
  2. When both conditions are met:
    • No errors found in Execution::Errors
    • Backtrace is enabled
  3. Then:
    • Wrap the error in GraphQL::Backtrace::TracedError
    • Return the wrapped error

This would provide consistent error handling while maintaining the backtrace functionality when enabled.

@rmosolgo
Copy link
Owner

rmosolgo commented Feb 5, 2025

Hey, thanks for reporting this and sorry for the trouble! Yes, I agree that I probably introduced it in #5217. I think your proposed solution sounds good. If you'd like to implement it, please give it a try. Otherwise, I'll take a look soon and follow up here 👍

@yn-misaki
Copy link
Author

Thank you for the quick response and reviewing my proposed solution! I'll try to implement solution 💡

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants