From 970dd17fe143ee7455bbc4869014d06fa1a93f6a Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Sat, 13 Sep 2025 06:53:17 -0400 Subject: [PATCH 1/3] Add debug info to UnresolvedTypeError --- lib/graphql/unresolved_type_error.rb | 21 ++++++++++++++++--- .../member/has_unresolved_type_error_spec.rb | 9 +++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/graphql/unresolved_type_error.rb b/lib/graphql/unresolved_type_error.rb index 3b8e95025b..30c817caad 100644 --- a/lib/graphql/unresolved_type_error.rb +++ b/lib/graphql/unresolved_type_error.rb @@ -24,11 +24,26 @@ def initialize(value, field, parent_type, resolved_type, possible_types) @parent_type = parent_type @resolved_type = resolved_type @possible_types = possible_types - message = "The value from \"#{field.graphql_name}\" on \"#{parent_type.graphql_name}\" could not be resolved to \"#{field.type.to_type_signature}\". " \ - "(Received: `#{resolved_type.inspect}`, Expected: [#{possible_types.map(&:graphql_name).join(", ")}]) " \ - "Make sure you have defined a `resolve_type` proc on your schema and that value `#{value.inspect}` " \ + abstract_type = field.type.unwrap + message = "The value from \"#{field.graphql_name}\" on \"#{parent_type.graphql_name}\" could not be resolved to \"#{abstract_type.to_type_signature}\". " \ + "(Received: `#{resolved_type.name ? resolved_type.inspect : resolved_type.graphql_name}`, Expected: [#{possible_types.map(&:graphql_name).join(", ")}]) " \ + "Make sure you have defined a `resolve_type` method on your schema and that value `#{value.inspect}` " \ "gets resolved to a valid type. You may need to add your type to `orphan_types` if it implements an " \ "interface but isn't a return type of any other field." + + if abstract_type.kind.interface? && (multiplex = Fiber[:__graphql_current_multiplex]) + types = multiplex.queries.first.types + if types.is_a?(Schema::Visibility::Profile) + visibility = types.instance_variable_get(:@visibility) + cached_vis = types.instance_variable_get(:@cached_visible) + message << "\n\n`#{abstract_type.graphql_name}.orphan_types`: #{abstract_type.orphan_types}" + impls = visibility.all_interface_type_memberships[abstract_type] + message << "\n`Schema.visibility.all_interface_type_memberships[#{abstract_type.graphql_name}]` (#{impls.size}):" + impls.each do |(impl_type, memberships)| + message << "\n - `#{impl_type.graphql_name}` | Object? #{impl_type.kind.object?} | referenced? #{types.send(:referenced?, impl_type)} | visible? #{cached_vis[impl_type]} | membership_visible? #{memberships.map { |itm| cached_vis[itm]}}" + end + end + end super(message) end end diff --git a/spec/graphql/schema/member/has_unresolved_type_error_spec.rb b/spec/graphql/schema/member/has_unresolved_type_error_spec.rb index b7bfc8f946..1c3fce7770 100644 --- a/spec/graphql/schema/member/has_unresolved_type_error_spec.rb +++ b/spec/graphql/schema/member/has_unresolved_type_error_spec.rb @@ -37,7 +37,7 @@ schema = Class.new(GraphQL::Schema) do query(query_type) - + use GraphQL::Schema::Visibility def self.resolve_type(abs_t, obj, ctx) ctx.schema.query end @@ -52,5 +52,12 @@ def self.resolve_type(abs_t, obj, ctx) schema.execute("{ anonInt { __typename } }") end assert_equal "GraphQL::UnresolvedTypeError", err.class.name + assert_equal <<~ERR.chomp, err.message + The value from "anonInt" on "Query" could not be resolved to "AnonInt". (Received: `Query`, Expected: [Obj]) Make sure you have defined a `resolve_type` method on your schema and that value `1` gets resolved to a valid type. You may need to add your type to `orphan_types` if it implements an interface but isn\'t a return type of any other field. + + `AnonInt.orphan_types`: [] + `Schema.visibility.all_interface_type_memberships[AnonInt]` (1): + - `Obj` | Object? true | referenced? true | visible? true | membership_visible? [true] + ERR end end From 69e8b2cfb0c4b83b69250b231e7b9d760ff49bac Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Sat, 13 Sep 2025 07:03:57 -0400 Subject: [PATCH 2/3] ignore lint --- lib/graphql/unresolved_type_error.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/graphql/unresolved_type_error.rb b/lib/graphql/unresolved_type_error.rb index 30c817caad..1ed0e943a3 100644 --- a/lib/graphql/unresolved_type_error.rb +++ b/lib/graphql/unresolved_type_error.rb @@ -32,7 +32,7 @@ def initialize(value, field, parent_type, resolved_type, possible_types) "interface but isn't a return type of any other field." if abstract_type.kind.interface? && (multiplex = Fiber[:__graphql_current_multiplex]) - types = multiplex.queries.first.types + types = multiplex.queries.first.types # rubocop:disable Development/ContextIsPassedCop if types.is_a?(Schema::Visibility::Profile) visibility = types.instance_variable_get(:@visibility) cached_vis = types.instance_variable_get(:@cached_visible) From acddb28f40f756d8940151bf97698f6e303d35b2 Mon Sep 17 00:00:00 2001 From: Robert Mosolgo Date: Sat, 13 Sep 2025 07:17:07 -0400 Subject: [PATCH 3/3] Duplicate frozen string --- lib/graphql/unresolved_type_error.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/graphql/unresolved_type_error.rb b/lib/graphql/unresolved_type_error.rb index 1ed0e943a3..15805f1cbf 100644 --- a/lib/graphql/unresolved_type_error.rb +++ b/lib/graphql/unresolved_type_error.rb @@ -34,6 +34,7 @@ def initialize(value, field, parent_type, resolved_type, possible_types) if abstract_type.kind.interface? && (multiplex = Fiber[:__graphql_current_multiplex]) types = multiplex.queries.first.types # rubocop:disable Development/ContextIsPassedCop if types.is_a?(Schema::Visibility::Profile) + message = message.dup visibility = types.instance_variable_get(:@visibility) cached_vis = types.instance_variable_get(:@cached_visible) message << "\n\n`#{abstract_type.graphql_name}.orphan_types`: #{abstract_type.orphan_types}"