Reject what a collection with multiple member types cannot coerce - #2955
Merged
Merged
Conversation
ericproulx
force-pushed
the
fix/variant-collection-coercion
branch
from
September 18, 2026 09:52
26ad6e4 to
ffc5d5a
Compare
Danger ReportNo issues found. |
ericproulx
force-pushed
the
fix/variant-collection-coercion
branch
2 times, most recently
from
September 18, 2026 10:30
6d49684 to
f17a6c4
Compare
ericproulx
force-pushed
the
fix/variant-collection-coercion
branch
from
September 18, 2026 12:52
f17a6c4 to
fb6068f
Compare
dblock
approved these changes
Sep 19, 2026
dblock
left a comment
Member
There was a problem hiding this comment.
If I understood the behavior change correctly, this is a bug? Maybe state so.
ericproulx
force-pushed
the
fix/variant-collection-coercion
branch
from
September 19, 2026 12:01
fb6068f to
ed5b3b6
Compare
A param declared as `type: Array[Integer, String]` -- or with such a collection among its `types:`, as in the README's `types: [Integer, String, Array[Integer, String]]` -- is coerced by VariantCollectionCoercer, which checked neither its input nor its output: - a value that is not an Array returned nil rather than an InvalidValue, so CoerceValidator wrote nil over it and `requires :ids, type: Array[Integer, String]` let `ids=x` through as nil; - each member went through the member coercer and the results were returned as they came, so a member none of the types accepts reached the endpoint as a Grape::Validations::Types::InvalidValue object. Array[Integer] answers 400 to both, and now so does a collection with multiple member types. nil and '' still coerce to nil, as specs pin. The coercion method, when one is given, was never called for anything but an Array: a `coerce_with` that splits a String into the collection saw nothing and the param came out nil. It is now handed the value whatever it is, as MultipleTypeCoercer hands it for `types:`. UPGRADING covers both, since each turns a request that used to pass into a 400. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
ericproulx
force-pushed
the
fix/variant-collection-coercion
branch
from
September 19, 2026 16:22
ed5b3b6 to
9186a50
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A param declared as
type: Array[Integer, String], or with such a collection in itstypes:(including the README'stypes: [Integer, String, Array[Integer, String]]), is coerced byVariantCollectionCoercer. That coercer checks neither its input nor its output:Array[Integer]Array[Integer, String]before{"ids":"x"}ids=nil(despiterequires){"ids":{"a":1}}nil{"ids":[1,{}]}[1, #<InvalidValue>]{"ids":[1,"2"]}[1, 2][1, 2]nilinstead of anInvalidValue, soCoerceValidatorwrotenilover the param.InvalidValueobject.Both now answer 400, like a single-type collection.
niland''still coerce tonil, as the existing specs pin.A
coerce_withmethod was also only ever called for an Array. A method that splits a String into the collection never ran, and the param came outnil. It now gets the value whatever it is, the same wayMultipleTypeCoercerhands it over fortypes:.The same behaviour shows up on 3.3.5 and 4.0.1. Found by fuzzing random
paramsschemas.Backward compatibility
Both changes turn requests that used to pass into 400s, so UPGRADING has an entry. The unit spec that pinned
call('not an array')→nilnow expects anInvalidValue.Members that are
nilare still accepted ([1, null]→[1, nil]), because the member coercer itself answersnilfor them.Array[Integer]rejects them. Changing that is a separate decision, so I left it alone.Test plan
coerce_validator_spec. All five new examples fail on master and pass here.spec/integration/hashienot run locally (hashie isn't installed). It only covers valid input, which takes the same path as before, so it's left to CI.🤖 Generated with Claude Code