Repository navigation
Fix find_signed for models with a composite primary key - #57245
Merged
fatkodima merged 1 commit intoApr 25, 2026
Merged
Conversation
Previously, `find_signed` raised `ArgumentError: Expected corresponding value for [...] to be an Array` for any model declaring a composite primary key (e.g. `self.primary_key = [:shop_id, :id]`). The verified id array was passed to `find_by(primary_key => id)`, which the predicate builder interprets as a request for multiple records and expects an array of arrays. `find_signed!` already worked because it dispatches through `find`, which zips the primary key with the id for composite keys via `find_one`. This brings `find_signed` in line with `find_signed!` and `find` while preserving relation scoping (so `Model.where(...).find_signed(token)` keeps working).
fatkodima
force-pushed
the
fix-find-signed-composite-primary-key
branch
from
April 25, 2026 17:16
a925a74 to
5070fbb
Compare
fatkodima
added a commit
that referenced
this pull request
Apr 25, 2026
…y-key Fix `find_signed` for models with a composite primary key
fatkodima
added a commit
that referenced
this pull request
Apr 25, 2026
…y-key Fix `find_signed` for models with a composite primary key
Contributor
Author
|
Thanks for the review! 😊 |
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
ActiveRecord::SignedId::ClassMethods#find_signedraises anArgumentErrorfor any model that declares a composite primary key (e.g.self.primary_key = [:shop_id, :id]). The bang variantfind_signed!works correctly. This PR makesfind_signedconsistent withfind_signed!andfind.The bug
Hits anyone using
find_signedon a CPK model — for example, a magic sign-in link, password reset, or email confirmation that scopes its token to such a record.Root cause
find_signedcallsat
signed_id.rb. For composite primary keys,primary_keyis an array (["shop_id", "id"]) and the verifiedidis also an array of values ([37647470, 75294940]). The predicate builder treats an array key as a request for multiple records and expects the value to be an array of arrays ([[shop_id, id], [shop_id, id], ...]). It iterates the inner values, finds non-arrays, and raises:predicate_builder.rb:
find_signed!does not hit this because it usesfind(id), which dispatches tofind_oneand zipsprimary_keywithidfor CPK atfinder_methods.rb.Note
Prior art
The same class of bug was fixed for
find_by_token_forin8617a7c(September 2024, by @fatkodima), wherefind_by(model.primary_key => id)was changed tofind_by(model.primary_key => [id])intoken_for.rb.signed_id.rbhas the identical pattern but was not updated at that time.Fix
Mirror the
find_onepattern infind_signed:find_by(rather thanfind) is preserved so relation scoping viaSignedId::RelationMethods#find_signedstill works (e.g.Cpk::Order.where(...).find_signed(token)).Note
Why
zipinstead of wrapping in an array?The
token_for.rbfix wraps the value in an extra array (find_by(primary_key => [id])), which works because the predicate builder interprets it as "find one record matching this composite key tuple." This PR instead usesprimary_key.zip(id).to_h, which produces a plain conditions hash (e.g.{ shop_id: 37647470, id: 75294940 }).I chose the
zipapproach because:find_one(finder_methods.rb), keeping the CPK handling consistent across the codebase.[id]requires understanding how the predicate builder disambiguates single-tuple vs multi-record lookups.Either approach produces the same query; this is purely a readability choice.
Tests
Three new tests in
signed_id_test.rbcover:find_signedround trip with CPKfind_signedon a relation with CPK (positive + negative scope)find_signed!round trip with CPK (regression guard)Added a CHANGELOG entry.
Without the fix, the first two fail with the
ArgumentErrorshown above; with the fix, the full file (38 tests, 56 assertions) passes. CI will exercise MySQL and PostgreSQL as well.