Repository navigation
Add Grape.config.allow_undeclared_params; false answers 400 to undeclared params - #3054
Merged
Merged
Conversation
ericproulx
force-pushed
the
feature/strict-params
branch
from
November 3, 2026 08:57
f4d2ad0 to
e0b1a15
Compare
Danger ReportNo issues found. |
ericproulx
force-pushed
the
feature/strict-params
branch
from
November 6, 2026 17:40
e0b1a15 to
e717009
Compare
Member
ericproulx
force-pushed
the
feature/strict-params
branch
from
November 7, 2026 12:44
e717009 to
580d9a6
Compare
ericproulx
added a commit
that referenced
this pull request
Nov 7, 2026
The spec added in #3051 stores `grape.errors.format` with `I18n.backend.store_translations`, which does not load the locale files. When the backend has not loaded them yet (the example runs first, or right after another spec's `I18n.reload!`), the first lookup loads en.yml over the stored translations and puts the default format back. `format` is the only key a spec stores that en.yml also defines, so this example is the only one affected. It fails on its own (`rspec spec/grape/exceptions/validation_errors_spec.rb:85`) and under seed 28071, which failed the rails_7_2 job on #3054. `I18n.backend.translations(do_init: true)` loads en.yml before the override is stored. `eager_load!` would too, but it also leaves the backend flagged as eager-loaded for the rest of the run, so every later `I18n.reload!` reloads at once, under whatever locales the spec has set at that moment. base_spec's `after { I18n.reload! }` runs while `available_locales` is `[:de, :jp]`, so the reload skipped `:en`, and every translation after it came back as its key: 1 to 80 failures depending on the seed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ericproulx
added a commit
that referenced
this pull request
Nov 7, 2026
The spec added in #3051 stores `grape.errors.format` with `I18n.backend.store_translations`, which does not load the locale files. When the backend has not loaded them yet (the example runs first, or right after another spec's `I18n.reload!`), the first lookup loads en.yml over the stored translations and puts the default format back. `format` is the only key a spec stores that en.yml also defines, so this example is the only one affected. It fails on its own (`rspec spec/grape/exceptions/validation_errors_spec.rb:85`) and under seed 28071, which failed the rails_7_2 job on #3054. `I18n.backend.translations(do_init: true)` loads en.yml before the override is stored. `eager_load!` would too, but it also leaves the backend flagged as eager-loaded for the rest of the run, so every later `I18n.reload!` reloads at once, under whatever locales the spec has set at that moment. base_spec's `after { I18n.reload! }` runs while `available_locales` is `[:de, :jp]`, so the reload skipped `:en`, and every translation after it came back as its key: 1 to 80 failures depending on the seed. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ericproulx
force-pushed
the
feature/strict-params
branch
from
November 7, 2026 12:52
580d9a6 to
1b5c416
Compare
dblock
approved these changes
Nov 8, 2026
ericproulx
force-pushed
the
feature/strict-params
branch
2 times, most recently
from
November 8, 2026 20:15
d27e783 to
601aeeb
Compare
…ared params With the setting false, a request carrying a param its endpoint did not declare is answered 400, the undeclared params reported in the same Grape::Exceptions::ValidationErrors as the other validation errors, with the message "is not allowed". Declared are the names of the endpoint's params blocks (its own and its namespaces', given blocks included, renamed params by the name sent), a contract's keys, the route's path params, and format. Hash, Array and JSON params are looked into key by key and named by path, as in items[1][price]. The check runs after the validators, so a JSON param sent as a String is looked into as the Hash it is coerced to. The declared names are folded into a frozen tree at compile!, held in Endpoint::State. A request with no query string and no body is let through without building params. The setting defaults to true, which keeps today's behaviour, and Grape 5.0 makes false the default. Fixes #2241. Refs #2416. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ericproulx
force-pushed
the
feature/strict-params
branch
from
November 8, 2026 20:27
601aeeb to
ce09d41
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.
Fixes #2241. Refs #2416.
Adds
Grape.config.allow_undeclared_params. Set tofalse, a request carrying a param its endpoint did not declare is answered 400. It defaults totrue, which keeps today's behaviour, and Grape 5.0 makesfalsethe default.Behaviour
Grape::Exceptions::ValidationErrorsas the other validation errors, sorescue_fromand error formatting work unchanged. The message is "is not allowed" (grape.errors.messages.undeclared), the same wording dry-schema uses for unknown keys.paramsreturns in every endpoint, and it would hide client typos (Fail on (or get list of) unknown parameters #2241).paramsblocks, its own and its namespaces'. That includesgivenblocks; a param renamed withas:counts by the name the client sends.contract's keys.format.items[1][price].type: HashorArray[String], takes anything.givenblocks accepts the keys of every declaration.JSONparam sent as a String is checked as the Hash it is coerced to.Implementation
Grape::Validations::UndeclaredParamsbuilds the declared names into a frozen tree atcompile!. The sources areroute_declared_params, each contract'skey_map.dump, and the route regexp's capture names. The tree is held inEndpoint::State#undeclared_params, which isnilwhile the setting istrue.Endpoint#run_validatorsadds its errors to the validators' errors.Request#params_sent?lets a request with no query string and no body through without buildingparams.Performance
These are paired medians. "
true" compares this branch with master; "false" compares the settingfalsewith ittrueon this branch.true, interpretertrue, YJITfalse, interpreterfalse, YJITWith the setting
true, there is no measurable cost.Putting path params into the tree at compile time matters for speed. Checking routing args per request sent every
:idroute down the slow path, at about +2.8 µs.Known limits (documented in the README)
oneof:param, and of a Hash, JSON or File param declared without a block, are not checked.declared(params)treats them the same way.version ..., using: :param, the version param counts as undeclared when it reaches an unversioned endpoint.false, a malformed query string gets a 400 even on an endpoint that never readsparams, because the check has to parse it.Towards 5.0
Setting it to
falseacross this repo's suite gives 90 failures. All of them are specs that send params they never declare. A common case is aparamsblock written for agetwhose siblingputdeclares nothing.Apps will hit the same thing, especially with tokens that only
beforefilters read. The UPGRADING entry tells users to set it tofalsein their test suite now and to declare such params. In 5.0 they can set it totrueto keep today's behaviour.Tests
spec/grape/validations/undeclared_params_spec.rb: 19 examples.spec/integration/dry_validation/dry_validation_spec.rb.bundle exec rakeis green. Thedry_validationgemfile suite is green too.🤖 Generated with Claude Code