Summary
mustJSONReader discards the json.Marshal error and returns an empty-body reader, silently sending a malformed or empty POST to the control plane when a value cannot be serialized.
Context
control_plane_memory_backend.go:402 defines func mustJSONReader(v any) io.Reader { data, _ := json.Marshal(v); return bytes.NewReader(data) }. When json.Marshal fails (unsupported type, cyclic struct, channel, function value), the error is thrown away and an empty bytes.NewReader([]byte(nil)) is returned. The downstream HTTP POST to the control plane sends an empty body, the store silently records nothing or overwrites with an empty value, and the real marshal error is never surfaced to the caller. This makes debugging data-loss bugs in the memory backend extremely difficult.
Scope
In Scope
- Rename to
jsonReader(v any) (io.Reader, error) and return the json.Marshal error.
- Update all call sites (
Set, Get, Delete) in control_plane_memory_backend.go to handle the error and propagate it to the caller.
- Remove the
must prefix convention from any helper in this file that can realistically fail.
Out of Scope
- Changing the
MemoryBackend interface signature beyond what is needed to propagate this error (context propagation is tracked in G2).
- Adding JSON schema validation or custom marshal logic.
Files
sdk/go/agent/control_plane_memory_backend.go:402 — replace mustJSONReader with jsonReader(v any) (io.Reader, error) and propagate the error
sdk/go/agent/control_plane_memory_backend.go — update all callers of the helper to handle and return the error
sdk/go/agent/control_plane_memory_backend_test.go — test: passing an unmarshalable value to Set returns an error rather than silently succeeding
Acceptance Criteria
Notes for Contributors
Severity: HIGH
Check the rest of the file for any other _, _ = json.Something or data, _ := patterns and fix those at the same time. The must naming convention should only be used for operations that genuinely cannot fail (e.g. parsing a compile-time constant) — not for anything that depends on user-provided data.
Summary
mustJSONReaderdiscards thejson.Marshalerror and returns an empty-body reader, silently sending a malformed or empty POST to the control plane when a value cannot be serialized.Context
control_plane_memory_backend.go:402definesfunc mustJSONReader(v any) io.Reader { data, _ := json.Marshal(v); return bytes.NewReader(data) }. Whenjson.Marshalfails (unsupported type, cyclic struct, channel, function value), the error is thrown away and an emptybytes.NewReader([]byte(nil))is returned. The downstream HTTP POST to the control plane sends an empty body, the store silently records nothing or overwrites with an empty value, and the real marshal error is never surfaced to the caller. This makes debugging data-loss bugs in the memory backend extremely difficult.Scope
In Scope
jsonReader(v any) (io.Reader, error)and return thejson.Marshalerror.Set,Get,Delete) incontrol_plane_memory_backend.goto handle the error and propagate it to the caller.mustprefix convention from any helper in this file that can realistically fail.Out of Scope
MemoryBackendinterface signature beyond what is needed to propagate this error (context propagation is tracked in G2).Files
sdk/go/agent/control_plane_memory_backend.go:402— replacemustJSONReaderwithjsonReader(v any) (io.Reader, error)and propagate the errorsdk/go/agent/control_plane_memory_backend.go— update all callers of the helper to handle and return the errorsdk/go/agent/control_plane_memory_backend_test.go— test: passing an unmarshalable value toSetreturns an error rather than silently succeedingAcceptance Criteria
Set(and any other caller of the serialization helper) returns a non-nil error when the value cannot be JSON-marshaledjson.Marshalerrors are silently discarded anywhere in the filemustJSONReaderfunction (or any equivalent silent-drop helper) no longer existsgo test ./sdk/go/...)make lint)Notes for Contributors
Severity: HIGH
Check the rest of the file for any other
_, _ = json.Somethingordata, _ :=patterns and fix those at the same time. Themustnaming convention should only be used for operations that genuinely cannot fail (e.g. parsing a compile-time constant) — not for anything that depends on user-provided data.