Skip to content

What error policy should explicit model registration use when an introspector raises? #130

Description

@numbata

Audited main at e62a44c142537c08eb144cd844ada6b893107b25 with Ruby 3.3.6. This is an open design question supported by a reproduction, not a request for a broad rewrite.

What and where

lib/grape_oas/api_model_builder.rb wraps both string constant resolution and introspector execution in rescue StandardError, then returns nil inside filter_map. A plugin programming error is indistinguishable from a model that has no handler: the requested schema disappears without a warning.

lib/grape_oas/introspectors/registry.rb already represents “no matching introspector” as nil; exceptions from a selected introspector therefore do not need to serve that purpose.

Minimal reproduction

Run with bundle exec ruby -Ilib:

require "grape_oas"
class BrokenIntrospector
  def self.handles?(subject) = subject == :broken
  def self.build_schema(*) = raise("introspector bug")
end
GrapeOAS.introspectors.register(BrokenIntrospector)
begin
  api = GrapeOAS::ApiModelBuilder.new(models: [:broken]).api
  p api.registered_schemas # [] -- no exception or warning
ensure
  GrapeOAS.introspectors.unregister(BrokenIntrospector)
end

The public models: generation option reaches this same builder path. The reproduction isolates it without needing an API route.

Why investigate

A registry is a useful strategy/plugin boundary only if it has a clear failure contract. Treating arbitrary execution failures as “unsupported input” hides defects in both built-in and third-party introspectors. Generation can report success while omitting models that consumers explicitly requested. Debugging requires tracing the silent rescue rather than seeing the original backtrace.

This is about exception scope and observability, not a blanket ban on rescue. Gracefully handling an unavailable optional integration is a different case.

Open questions and options

  • Should explicit models: entries fail generation when resolution or schema construction fails?
  • If compatibility requires best-effort output, should it emit a contextual warning and offer a strict mode?
  • Should an unknown constant and an exception raised inside a matching introspector have different policies? Keep any expected constant lookup rescue tightly scoped so a plugin's internal NameError is not swallowed accidentally.
  • Should a model with no matching handler also be reported, given that the user explicitly requested it?

Prefer preserving the exception and its cause for unexpected plugin failures. Add regression cases for missing model names, unsupported models, and a matching introspector raising RuntimeError or NoMethodError. Valid models must retain their output. Release triage: worthwhile correctness/diagnostic hardening, but switching from silent omission to raising is an observable compatibility change and needs an explicit decision and changelog.

References

Ruby exception handling explains rescue matching and exception propagation. Ruby permits broad rescue; the concern here is the information lost at this particular plugin boundary.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions