ruby-grape/grape · error · ArgumentError
oneof: must be a non-empty Array of blocks
Error message
oneof: must be a non-empty Array of blocks
What it means
`process_oneof!` requires the `oneof:` value to be a non-empty Array so there is at least one alternative to validate against. `nil`, an empty Array, or a bare (unwrapped) Proc all fail `variants.is_a?(Array) && variants.any?` and raise ArgumentError at API-definition time.
Source
Thrown at lib/grape/validations/params_scope.rb:407
# configuration[:some_type] evaluates to nil. Skipping instantiation
# here is correct — the real mounted instance will replay this step
# with the actual type value.
return unless coerce_options.type
validate('coerce', coerce_options, attrs, spec.required?, spec.shared_opts)
end
# Translate a `oneof: [proc, proc, ...]` declaration into a list of
# captured validator arrays — one array per variant. Each variant's
# block is evaluated in its own +ParamsScope+ backed by an
# {OneofCollector} so the full params DSL is available inside variants
# and the resulting validators are kept out of the real API's
# registration list.
def process_oneof!(validations)
raise ArgumentError, 'oneof: requires type: Hash' unless validations[:type] == Hash
variants = validations[:oneof]
raise ArgumentError, 'oneof: must be a non-empty Array of blocks' unless variants.is_a?(Array) && variants.any?
raise ArgumentError, 'oneof: each variant must be a Proc' unless variants.all?(Proc)
validations[:oneof] = variants.map { |block| OneofCollector.collect(block) }
end
def validate(type, options, attrs, required, opts)
validator_class = Validations.require_validator(type)
validator_instance = validator_class.new(
attrs,
options,
required,
self,
opts
)
@api.inheritable_setting.add_validation(validator_instance)
end
def all_element_blank?(scoped_params)View on GitHub (pinned to 22d7975629)
Solutions
- Wrap variants in an Array with at least one element: `oneof: [-> { requires :id }, -> { requires :name }]`
- Wrap a single alternative: `oneof: [-> { requires :id }]`
- If there are no real alternatives, delete the `oneof:` key entirely
Example fix
# before
requires :filter, type: Hash, oneof: variants # variants == [] at load time
# after
requires :filter, type: Hash, oneof: [
-> { requires :id, type: Integer },
-> { requires :name, type: String }
] Defensive patterns
Strategy: validation
Validate before calling
def oneof_variants_present?(opts) variants = opts[:oneof] variants.is_a?(Array) && !variants.empty? end raise ArgumentError, 'oneof needs at least one variant' unless oneof_variants_present?(filter_opts)
Try / catch
begin
requires :filter, type: Hash, oneof: build_variants # may return []
rescue ArgumentError => e
raise "oneof built no variants for :filter: #{e.message}"
end Prevention
- When generating variant lists dynamically, fall back to a normal declaration when the list is empty
- Wrap single alternatives in `[...]` by convention
- Spec-test generated declarations for every branch that builds oneof lists
When it happens
Trigger: `oneof: []`; `oneof: nil`; `requires :filter, type: Hash, oneof: -> { requires :id }` (a single proc not wrapped in an Array).
Common situations: Dynamically building the variant list and passing through an empty result when no variants apply; forgetting the extra brackets around a single alternative.
Related errors
- oneof: requires type: Hash
- oneof: each variant must be a Proc
- :type may not be supplied with :types
- must supply type for coerce_with
- coerce_with disallowed for type: JSON
AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21).
Data as JSON: /api/errors/63126d1255fb5c98.
Report an issue: GitHub.