Skip to content

fix(firestore): scope the Decoder document reference to each decode call - #16835

Open
paulb777 wants to merge 3 commits into
mainfrom
pb-firestore-decoder-scoped-docref
Open

paulb777 wants to merge 3 commits into
mainfrom
pb-firestore-decoder-scoped-docref

Conversation

@paulb777

@paulb777 paulb777 commented Oct 2, 2026

Copy link
Copy Markdown
Member

Problem

Firestore.Decoder.decode(_:from:in:) saved the DocumentReference in the decoder's public
userInfo and never removed it. @DocumentID reads the reference from userInfo. So when
an app reuses one Firestore.Decoder instance, a later call with in: nil, or a call to
decode(_:from:), filled @DocumentID with the ID of the previously decoded document. It did
not fail the way a new decoder does. A side effect was that every referenced decode also
modified the caller's userInfo.

This regressed in Firebase 10 (#9465). The 9.x decoder kept the reference scoped to the call.

Change

  • decode(_:from:in:) now adds the reference to a local copy of userInfo and passes that
    copy to a private decode(_:from:userInfo:) helper. decode(_:from:) uses the same helper
    with the decoder's own userInfo.
  • The decoder's public userInfo is no longer modified. User-provided entries are still
    passed through.
  • No API change. Decoding without a reference now behaves the same for new and reused
    decoders: decoding a type with @DocumentID throws
    FirestoreDecodingError.decodingIsNotSupported, as a new decoder already did.
  • CHANGELOG entry under # Unreleased.

Testing

New Swift Testing suite in the SPM unit target:
Firestore/Swift/Tests/Unit/Codable/FirestoreDecoderTests.swift.

  • reusedDecoderDoesNotReusePreviousDocumentReference: decodes with a reference, then
    decodes on the same decoder with in: nil and with decode(_:from:).
  • decodingWithDocumentReferenceDoesNotModifyUserInfo: checks that custom userInfo is
    passed through and left unchanged.
mkdir -p .swiftpm/xcode/xcshareddata/xcschemes && cp scripts/spm_test_schemes/* .swiftpm/xcode/xcshareddata/xcschemes/
xcodebuild -scheme FirebaseFirestoreTests test -destination 'platform=macOS' -jobs 4 \
  -only-testing:FirebaseFirestoreTests/FirestoreDecoderTests
  • Before the fix, both tests fail. The reused decoder returned
    Model(name: "second", docId: "first") for in: nil and for decode(_:from:), and
    decoder.userInfo.count was 2.
  • After the fix, both pass. The full FirebaseFirestoreTests scheme on macOS passes
    (8 tests in 3 suites).
  • As a temporary local check (not committed), I copied the existing
    Firestore/Swift/Tests/Codable tests into the SPM test target with an FSTTestDocRef shim
    and ran them in Swift 5 mode. All 43 of them pass with the fix.

Fixes #16789

`Firestore.Decoder.decode(_:from:in:)` stored the document reference in the
decoder's public `userInfo` and never removed it. When an app reused a decoder,
a later call with `in: nil`, or a call to `decode(_:from:)`, populated
`@DocumentID` properties with the ID of the previously decoded document instead
of failing like a new decoder does. Every decode with a reference also mutated
the caller's `userInfo`.

The reference is now added to a per-call copy of `userInfo`, so the decoder's
own `userInfo` is never modified and decoding without a reference behaves the
same for new and reused decoders.

Fixes #16789
@gemini-code-assist

Copy link
Copy Markdown
Contributor
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

@paulb777

paulb777 commented Oct 2, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request fixes a bug in Firestore.Decoder where a reused decoder incorrectly persisted and reused a DocumentReference from a previous decoding call to populate @DocumentID properties in subsequent calls. This is resolved by scoping the userInfo modification to the individual decode call. Unit tests have been added to verify this behavior. The reviewer suggested using the parameter t instead of T.self in the decode call on line 93 for consistency with other overloads.

Comment thread Firestore/Swift/Source/Codable/EncoderDecoder.swift Outdated
…_:from:in:)

Decode the metatype that the caller passes in, like decode(_:from:) does, instead of T.self, as suggested in review. The only internal caller passes T.self, so its behavior doesn't change.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Firestore.Decoder can reuse a stale DocumentReference when the next reference is nil

3 participants