Summary
clientgen emits the options-aware UnmarshalJSONSebuf(data, opts) twin for every message emitter. httpgen does not. After #212 (which fixed the unwrap and bytes_encoding emitters), five httpgen emitters still emit only the stdlib UnmarshalJSON(data []byte) error:
| Emitter |
Site |
Golden types with no twin |
internal/httpgen/empty_behavior.go |
:237 |
Response |
internal/httpgen/flatten.go |
:291 |
DualFlatten, MixedFlatten, SimpleFlatten |
internal/httpgen/nullable.go |
:187 |
User |
internal/httpgen/timestamp_format.go |
:220 |
TimestampFormatTest |
internal/httpgen/oneof_discriminator.go |
:289 |
FlattenedEvent, NestedEvent |
Enums (enum_encoding.go:159) are correctly exempt — protojson options act on message fields and an enum scalar has none.
Why it matters
The generated Go client dispatches sebufUnmarshaler → json.Unmarshaler → opts.Unmarshal (internal/clientgen/generator.go:926). A type with only the stdlib half lands on the middle branch, which cannot carry protojson.UnmarshalOptions, so With<Service>DiscardUnknownFields(true) is silently dropped. This is exactly #204, in five more places.
Severity: latent, order-dependent
Unlike #204, this does not always bite. Both plugins write the same filenames (<prefix>_flatten.pb.go, <prefix>_encoding.pb.go, …), so under buf the last-listed plugin's file wins:
go-client listed after go-http (as in every example today) → clientgen's correct copy ships, bug invisible.
go-http listed after go-client → httpgen's copy ships, DiscardUnknown is dropped.
go-http only → no Go client exists, so the twin is unused.
So it bites a specific-but-legitimate plugin ordering. See #236 for the underlying filename collision, which is why this class keeps recurring.
Fix
Same three-line pattern #212 applied: rename the emitted method to UnmarshalJSONSebuf(data []byte, opts protojson.UnmarshalOptions), thread opts.Unmarshal where the body currently ends in a bare protojson.Unmarshal, and emit the thin UnmarshalJSON wrapper delegating with protojson.UnmarshalOptions{}. Then regenerate the affected goldens.
Guard
internal/httpgen/unwrap_pairing_test.go already checks the pairing invariant, but its file filter is scoped to _unwrap.pb.go and _encoding.pb.go. Widening it to every *.pb.go golden (the value-receiver enum exemption already handles enums) turns it into the real drift guard — and it fails on the 8 types above until this issue is closed. #212 lands a skip list naming these files and pointing here; closing this issue means deleting that list.
Related: #204, #212.
Summary
clientgenemits the options-awareUnmarshalJSONSebuf(data, opts)twin for every message emitter.httpgendoes not. After #212 (which fixed theunwrapandbytes_encodingemitters), five httpgen emitters still emit only the stdlibUnmarshalJSON(data []byte) error:internal/httpgen/empty_behavior.goResponseinternal/httpgen/flatten.goDualFlatten,MixedFlatten,SimpleFlatteninternal/httpgen/nullable.goUserinternal/httpgen/timestamp_format.goTimestampFormatTestinternal/httpgen/oneof_discriminator.goFlattenedEvent,NestedEventEnums (
enum_encoding.go:159) are correctly exempt — protojson options act on message fields and an enum scalar has none.Why it matters
The generated Go client dispatches
sebufUnmarshaler→json.Unmarshaler→opts.Unmarshal(internal/clientgen/generator.go:926). A type with only the stdlib half lands on the middle branch, which cannot carryprotojson.UnmarshalOptions, soWith<Service>DiscardUnknownFields(true)is silently dropped. This is exactly #204, in five more places.Severity: latent, order-dependent
Unlike #204, this does not always bite. Both plugins write the same filenames (
<prefix>_flatten.pb.go,<prefix>_encoding.pb.go, …), so underbufthe last-listed plugin's file wins:go-clientlisted aftergo-http(as in every example today) → clientgen's correct copy ships, bug invisible.go-httplisted aftergo-client→ httpgen's copy ships,DiscardUnknownis dropped.go-httponly → no Go client exists, so the twin is unused.So it bites a specific-but-legitimate plugin ordering. See #236 for the underlying filename collision, which is why this class keeps recurring.
Fix
Same three-line pattern #212 applied: rename the emitted method to
UnmarshalJSONSebuf(data []byte, opts protojson.UnmarshalOptions), threadopts.Unmarshalwhere the body currently ends in a bareprotojson.Unmarshal, and emit the thinUnmarshalJSONwrapper delegating withprotojson.UnmarshalOptions{}. Then regenerate the affected goldens.Guard
internal/httpgen/unwrap_pairing_test.goalready checks the pairing invariant, but its file filter is scoped to_unwrap.pb.goand_encoding.pb.go. Widening it to every*.pb.gogolden (the value-receiver enum exemption already handles enums) turns it into the real drift guard — and it fails on the 8 types above until this issue is closed. #212 lands a skip list naming these files and pointing here; closing this issue means deleting that list.Related: #204, #212.