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.
Audited main at
e62a44c142537c08eb144cd844ada6b893107b25with 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 returnsnilinsidefilter_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: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
models:entries fail generation when resolution or schema construction fails?NameErroris not swallowed accidentally.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
RuntimeErrororNoMethodError. 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.