Just upgraded from 1.9.18 (with no issues) and now I am getting an error when passing in null for a nested input argument (full example below).
This arises when:
null for the nested input typeIn my case, I don't want to just leave out the nested input, it is a related record that I want to explicitly set as null at the model level.
Downgrading to 1.9.18 fixes this behavior, so I think it's a bug! 馃悰
GraphQL Ruby: 1.10.0
Ruby: 2.6.5
Rails: 6.0.2.1
mutation_type.rb
module Types
class MutationType < GraphQL::Schema::Object
field :test_mutation, mutation: Mutations::TestMutation
end
end
test_mutation.rb
module Mutations
class TestMutation < Mutations::BaseMutation
argument :params, Types::TestInput, required: true
type Boolean
def resolve(params:)
p params
true
end
end
end
test_input.rb
module Types
class TestInput < Types::BaseInputObject
argument :example, String, required: false
argument :another_input_type, Types::AnotherInput, required: false
end
end
another_input.rb
module Types
class AnotherInput < Types::BaseInputObject
argument :another_example, String, required: false
argument :yet_another_example, Int, required: false
end
end
Query:
mutation test($params: TestInput!) {
testMutation(params: $params)
}
Params:
{
"params": {
"example": "hi",
"anotherInputType": null
}
}
Note that I'm passing in null for anotherInputType above...
{
"error": {
"message": "undefined method `key?' for nil:NilClass",
"backtrace": [
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/schema/input_object.rb:194:in `block in coerce_input'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/schema/input_object.rb:190:in `each'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/schema/input_object.rb:190:in `coerce_input'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/schema/input_object.rb:208:in `block in coerce_input'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/schema/input_object.rb:190:in `each'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/schema/input_object.rb:190:in `coerce_input'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/schema/non_null.rb:54:in `coerce_input'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/query/variables.rb:42:in `block (2 levels) in initialize'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/errors.rb:41:in `with_error_handling'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/query/variables.rb:41:in `block in initialize'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/query/variables.rb:19:in `each'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/query/variables.rb:19:in `each_with_object'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/query/variables.rb:19:in `initialize'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/query.rb:226:in `new'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/query.rb:226:in `block in variables'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/query.rb:396:in `with_prepared_ast'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/query.rb:225:in `variables'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/query/validation_pipeline.rb:80:in `ensure_has_validated'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/query/validation_pipeline.rb:35:in `valid?'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/query.rb:261:in `valid?'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/analysis/ast.rb:31:in `block (2 levels) in analyze_multiplex'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/analysis/ast.rb:30:in `map'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/analysis/ast.rb:30:in `block in analyze_multiplex'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/tracing.rb:66:in `block in trace'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/tracing.rb:80:in `call_tracers'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/tracing.rb:66:in `trace'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/analysis/ast.rb:29:in `analyze_multiplex'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/multiplex.rb:195:in `block in instrument_and_analyze'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/instrumentation.rb:29:in `block (2 levels) in apply_instrumenters'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/instrumentation.rb:46:in `block (2 levels) in each_query_call_hooks'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/instrumentation.rb:41:in `each_query_call_hooks'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/instrumentation.rb:45:in `block in each_query_call_hooks'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/instrumentation.rb:72:in `call_hooks'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/instrumentation.rb:44:in `each_query_call_hooks'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/instrumentation.rb:27:in `block in apply_instrumenters'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/instrumentation.rb:72:in `call_hooks'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/instrumentation.rb:26:in `apply_instrumenters'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/multiplex.rb:175:in `instrument_and_analyze'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/multiplex.rb:61:in `block in run_queries'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/tracing.rb:66:in `block in trace'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/tracing.rb:80:in `call_tracers'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/tracing.rb:66:in `trace'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/multiplex.rb:59:in `run_queries'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/execution/multiplex.rb:49:in `run_all'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/schema.rb:1541:in `multiplex'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/graphql-1.10.0/lib/graphql/schema.rb:1512:in `execute'",
"/Users/me/app/controllers/graphql_controller.rb:8:in `execute'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/abstract_controller/base.rb:196:in `process_action'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/scout_apm-2.6.6/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb:73:in `process_action'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_controller/metal/rendering.rb:30:in `process_action'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/abstract_controller/callbacks.rb:42:in `block in process_action'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/activesupport-6.0.2.1/lib/active_support/callbacks.rb:135:in `run_callbacks'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/abstract_controller/callbacks.rb:41:in `process_action'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_controller/metal/rescue.rb:22:in `process_action'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_controller/metal/instrumentation.rb:33:in `block in process_action'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/activesupport-6.0.2.1/lib/active_support/notifications.rb:180:in `block in instrument'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/activesupport-6.0.2.1/lib/active_support/notifications/instrumenter.rb:24:in `instrument'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/activesupport-6.0.2.1/lib/active_support/notifications.rb:180:in `instrument'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_controller/metal/instrumentation.rb:32:in `process_action'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_controller/metal/params_wrapper.rb:245:in `process_action'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/activerecord-6.0.2.1/lib/active_record/railties/controller_runtime.rb:27:in `process_action'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/scout_apm-2.6.6/lib/scout_apm/instruments/action_controller_rails_3_rails4.rb:86:in `process_action'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/abstract_controller/base.rb:136:in `process'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionview-6.0.2.1/lib/action_view/rendering.rb:39:in `process'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_controller/metal.rb:191:in `dispatch'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_controller/metal.rb:252:in `dispatch'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/routing/route_set.rb:51:in `dispatch'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/routing/route_set.rb:33:in `serve'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/journey/router.rb:49:in `block in serve'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/journey/router.rb:32:in `each'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/journey/router.rb:32:in `serve'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/routing/route_set.rb:837:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/scout_apm-2.6.6/lib/scout_apm/instruments/rails_router.rb:29:in `call_with_scout_instruments'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/scout_apm-2.6.6/lib/scout_apm/instant/middleware.rb:53:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/scout_apm-2.6.6/lib/scout_apm/middleware.rb:17:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/rack-attack-6.2.2/lib/rack/attack.rb:156:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/rack-attack-6.2.2/lib/rack/attack.rb:170:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/warden-1.2.8/lib/warden/manager.rb:36:in `block in call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/warden-1.2.8/lib/warden/manager.rb:34:in `catch'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/warden-1.2.8/lib/warden/manager.rb:34:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/bundler/gems/rack-f690bb71425a/lib/rack/tempfile_reaper.rb:17:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/bundler/gems/rack-f690bb71425a/lib/rack/etag.rb:27:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/bundler/gems/rack-f690bb71425a/lib/rack/conditional_get.rb:40:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/bundler/gems/rack-f690bb71425a/lib/rack/head.rb:14:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/http/content_security_policy.rb:18:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/bundler/gems/rack-f690bb71425a/lib/rack/session/abstract/id.rb:269:in `context'",
"/Users/me/.rvm/gems/ruby-2.6.5/bundler/gems/rack-f690bb71425a/lib/rack/session/abstract/id.rb:263:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/cookies.rb:648:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/activerecord-6.0.2.1/lib/active_record/migration.rb:567:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/activesupport-6.0.2.1/lib/active_support/callbacks.rb:101:in `run_callbacks'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/callbacks.rb:26:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/executor.rb:14:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/actionable_exceptions.rb:17:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/rollbar-2.23.2/lib/rollbar/middleware/rails/rollbar.rb:25:in `block in call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/rollbar-2.23.2/lib/rollbar.rb:145:in `scoped'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/rollbar-2.23.2/lib/rollbar/middleware/rails/rollbar.rb:22:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/debug_exceptions.rb:32:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/rollbar-2.23.2/lib/rollbar/middleware/rails/show_exceptions.rb:22:in `call_with_rollbar'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/web-console-4.0.1/lib/web_console/middleware.rb:132:in `call_app'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/web-console-4.0.1/lib/web_console/middleware.rb:28:in `block in call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/web-console-4.0.1/lib/web_console/middleware.rb:17:in `catch'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/web-console-4.0.1/lib/web_console/middleware.rb:17:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/lograge-0.11.2/lib/lograge/rails_ext/rack/logger.rb:15:in `call_app'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/railties-6.0.2.1/lib/rails/rack/logger.rb:26:in `block in call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/activesupport-6.0.2.1/lib/active_support/tagged_logging.rb:80:in `block in tagged'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/activesupport-6.0.2.1/lib/active_support/tagged_logging.rb:28:in `tagged'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/activesupport-6.0.2.1/lib/active_support/tagged_logging.rb:80:in `tagged'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/railties-6.0.2.1/lib/rails/rack/logger.rb:26:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/sprockets-rails-3.2.1/lib/sprockets/rails/quiet_assets.rb:13:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/request_store-1.5.0/lib/request_store/middleware.rb:19:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/request_id.rb:27:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/bundler/gems/rack-f690bb71425a/lib/rack/method_override.rb:24:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/bundler/gems/rack-f690bb71425a/lib/rack/runtime.rb:24:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/activesupport-6.0.2.1/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/executor.rb:14:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/static.rb:126:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/bundler/gems/rack-f690bb71425a/lib/rack/sendfile.rb:113:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/actionpack-6.0.2.1/lib/action_dispatch/middleware/host_authorization.rb:77:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/webpacker-4.2.2/lib/webpacker/dev_server_proxy.rb:23:in `perform_request'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/rack-proxy-0.6.5/lib/rack/proxy.rb:57:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/scout_apm-2.6.6/lib/scout_apm/instruments/middleware_summary.rb:58:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/railties-6.0.2.1/lib/rails/engine.rb:526:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/puma-4.3.1/lib/puma/configuration.rb:228:in `call'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/puma-4.3.1/lib/puma/server.rb:681:in `handle_request'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/puma-4.3.1/lib/puma/server.rb:472:in `process_client'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/puma-4.3.1/lib/puma/server.rb:328:in `block in run'",
"/Users/me/.rvm/gems/ruby-2.6.5/gems/puma-4.3.1/lib/puma/thread_pool.rb:134:in `block in spawn_thread'"
]
},
"data": {}
}
I think the coerce_input object in input_object.rb:194 needs to check if the input is nil before calling the key method on it.
Thanks for the detailed report here, sorry about the hangup!
I agree with your suggested fix. It used to be that the graphql runtime applied those null checks, but in 1.10, the types themselves are responsible for it. You can see the same check here in List, which I _didn't_ forget:
But, it looks like I missed InputObject!
Are you interested in taking a try at the fix? I think your suggestion is spot-on.
@rmosolgo sure thing I can hopefully have something up today 馃憤
Hey @rmosolgo -- @hueter and I tried to figure out the best place to add the spec, but we weren't sure given that there are a number of files and the changes to specs for this file didn't seem to run this method. I did, however, add spec coverage to our repo and was able to both reproduce the error we were receiving and confirm that adding return nil if value.nil? to input_object.rb on line 193 resolves the issue. Let us know the best way to proceed, but we'll keep our test coverage in place for now and look for the next version that fixes this!
Most helpful comment
Thanks for the detailed report here, sorry about the hangup!
I agree with your suggested fix. It used to be that the graphql runtime applied those null checks, but in 1.10, the types themselves are responsible for it. You can see the same check here in
List, which I _didn't_ forget:https://github.com/rmosolgo/graphql-ruby/blob/35b96c4f86a9a029dd2d94717a3c08ddf9cd4739/lib/graphql/schema/list.rb#L39-L41
But, it looks like I missed InputObject!
Are you interested in taking a try at the fix? I think your suggestion is spot-on.