[AsParameters]: reject unparseable values in collection query parameters (GH-3398)#3404
Merged
Merged
Conversation
…ers (GH-3398) GH-3372 made a present-but-unparseable *scalar* query string value short circuit with a 400 ProblemDetails under the opt-in WolverineHttpOptions.RejectUnparseableQueryValues. Collections were left as a noted follow-up, and they are the worse failure mode: an unparseable element was silently dropped, taking an optional filter collection to null (or to a partial list), so a filtering endpoint answered 200 with an *unfiltered* result set while the caller believed the results were filtered. FluentValidation cannot compensate because it only ever sees null. ParsedArrayQueryStringValue (T[]) and ParsedCollectionQueryStringValue (List<T>/IList<T>/IReadOnlyList<T>/IEnumerable<T>) now take the same rejectUnparseableValue flag as ReadHttpFrame and emit a shared rejection block: a present but unparseable element writes the 400 ProblemDetails naming the parameter and exits. Binding is all-or-nothing -- one bad element rejects the whole collection rather than silently keeping the parseable ones. An omitted parameter still keeps its initializer in both modes, string collections are untouched, and the flag remains opt-in (default false) until the 7.0 flip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 14, 2026
This was referenced Jul 18, 2026
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.
Closes #3398. For 6.18.0. Reported by @outofrange-consulting — thank you. (Supersedes #3402, which was accidentally cut on top of another branch; identical commit, rebased onto main.)
The bug
An unparseable value in a collection query parameter silently bound
nullinstead of returning 400:?colours=Purple(invalid enum) → 200 OK,Colours == null?colours=Red&colours=Purple→ 200 OK,Colours == null— the validRedis dropped tooint[]/Guid[]/long[].nullis indistinguishable from "parameter omitted", so on a filter endpoint this silently drops the predicate and returns an unfiltered 200. FluentValidation cannot compensate: it seesnull, soRuleForEachhas nothing to iterate. A silently wrong answer is the worst failure mode available here.Root cause
Two codegen frames handle collection binding and neither knew about the #3372 strict flag:
CodeGen/ParsedArrayQueryStringValue.cs(T[])CodeGen/QueryStringHandling.cs→ParsedCollectionQueryStringValue(List<T>/IList<T>/IReadOnlyList<T>/IEnumerable<T>)Both emit
foreach+if (TryParse(...)) list.Add(...)with no else, so a bad element is simply dropped. The array frame then doesif (list.Any()) prop = list.ToArray();— so an all-bad collection leaves the propertynull, and a mixed one silently binds a partial filter.The fix — completes #3372
#3372 fixed the scalar case (shipped 6.17.1/6.17.2). Both collection frames now take the same
rejectUnparseableValueflag, wired fromWolverineHttpOptions.RejectUnparseableQueryValuesthroughHttpChain.TryFindOrCreateQuerystringValue, and emit a sharedStrictQueryBinding.WriteRejectionBlockthat calls the existingHttpHandler.WriteQueryValueParsingProblem.Semantics mirror the scalar case exactly:
All-or-nothing, deliberately
One bad element rejects the whole collection. Binding
[Red]from?colours=Red&colours=Purplewould still be exactly the silently-wrong-answer this issue is about — the caller asked to filter on two values and would get results filtered on one, with a 200 and no signal. Minimal APIs likewise fail the request rather than hand back a partial collection. Rationale is in the docs too, not just here.Verification
Failed: 6, Passed: 17) while the lenient/omitted/valid tests already passed — confirming the change is confined to the flag and cannot alter default behavior.strict_query_string_binding23/23.Wolverine.Http.Tests844 passed, 0 failed (net9.0).wolverine.slnxRelease build: 0 warnings, 0 errors.as-parameters.mdgains a Collections subsection (behavior matrix + rationale);querystring.mdand theRejectUnparseableQueryValuesXML doc note collection coverage.🤖 Generated with Claude Code