fix: omit proto3 implicit-presence defaults when encoding from parse().root#2345
Merged
dcodeIO merged 2 commits intoJul 6, 2026
Conversation
…).root A proto3 singular scalar set to its default value was serialized onto the wire when the type was built via parse(proto).root or new Root().addJSON() and encoded without a prior resolveAll(). Type#setup() (the lazy initializer that builds the encoder on first encode) resolved field types but not feature defaults, so field presence was still unresolved when the encoder was generated. Field#hasPresence then read an undefined field_presence feature and returned true, making the encoder treat an implicit-presence field as explicit and emit its default value. Resolve feature defaults in setup() before generating the codecs, the same resolution load()/loadSync()/fromJSON() already perform. proto3 implicit fields now omit defaults; proto2, proto3 optional, and editions explicit-presence fields are unaffected.
dcodeIO
approved these changes
Jul 5, 2026
Member
|
Thanks! |
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.
A proto3 singular scalar set to its default value is incorrectly serialized onto the wire when the type is built via
parse(proto).rootornew Root().addJSON(json)and encoded without first callingresolveAll().Calling
root.resolveAll()first (or usingload()/loadSync()/fromJSON(), which resolve internally) produces the correct[], so only the lazyparse().rootencode path is affected.Cause
Type#setup()— the lazy initializer that builds the encoder on firstencode()— resolves field types (field.resolve()) but never resolves feature defaults. So at codegen timefield._features.field_presenceis stillundefined, andField#hasPresencereturnsundefined !== "IMPLICIT"→true. The generated encoder then takes the presence-tracking branch (no default-value check) for an implicit-presence field and emits its default.Impact
Non-canonical proto3 output from the most common inline-proto path: larger payloads, and byte-mismatches against peers that omit defaults (Go/C++/Python, and protobuf.js's own
load()), which breaks deterministic serialization, signing/hashing of serialized bytes, and golden-file/conformance comparisons. Decoding is unaffected.Fix
Resolve feature defaults in
setup()before the codecs are generated — the same resolutionload()/loadSync()/fromJSON()already perform — soparse().rootproduces the same wire form. Verified across syntaxes: proto3 implicit fields now omit defaults ([]); proto2, proto3optional, and editions explicit-presence fields still emit them ([8,0]);-0.0is still preserved.Tests
Added a
comp_optionalcase mirroring the existing "implicit scalar conversion defaults" test but without theresolveAll()call. It fails on the current code ([8,0]…) and passes with the fix. Fulltest:sourcesgreen (2723 passing, 0 failing); no regressions.