Conversation
…eld messages Zero-field types emit no field loop, so `t` was never shifted to the field number before the unknown-fields call. The generated decoder then asked skipType to match the full tag against the end-group field number, which can never succeed — well-formed unknown groups (and editions DELIMITED fields) threw "invalid end group tag" instead of being skipped. Fixes protobufjs#2431.
JavaGT
force-pushed
the
fix/decoder-unknown-group-zero-fields
branch
from
September 13, 2026 02:20
2cba437 to
844703e
Compare
This branch has not been deployed
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 #2431.
What changed
One line in the generated-decoder codegen, plus a regression test.
Zero-field message types (and editions DELIMITED fields) threw
invalid end group tagwhen decoding bytes containing a well-formed unknown group, instead of skipping it:Why
The generated field loop shifts
tto the field number viaswitch(t >>> 3), but that switch only exists when the type has fields. For zero-field types, the unknown-fields callr.skipType(t&7, q, t)passed the full tag (fieldNumber*8 + wireType) as the expected field number, so the end-group check inReader.prototype.skipTypecould never match. C++ and other conformant runtimes accept (and are required to accept) unknown groups with matching end tags, so this breaks the "empty message as a marker; schema evolved later" case.The fix
When the type has no fields, shift the tag in the generated expression:
r.skipType(t&7, q, t>>>3). Types with fields are untouched (tis already the field number there). Mismatched end tags are still rejected — the new test covers both.Verification
tests/api_decoder_bounds.js): zero-field message accepts a matching end tag, still rejects a mismatched one, and a one-field message behaves unchanged.npm testpasses (sources + types) on this branch.AI transparency
Found during an agent-run improvement audit of a downstream consumer's dependency. Exploration/verification: GPT-5.6 Luna and Grok 4.6 via OpenCode CLI (both independently confirmed the bug by executing the repro). Planning and implementation: GLM (ZCode agent). Verified against master at 0ad9a28.