Skip to content

[BUG] httpgen: five emitters still lack the UnmarshalJSONSebuf twin, so DiscardUnknown is dropped #235

Description

@SebastienMelki

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 sebufUnmarshalerjson.Unmarshaleropts.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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingconsistencyCross-generator consistency issuegen/go-httpprotoc-gen-go-http (HTTP server)json-mappingJSON serialization mapping features

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions