Go: return an empty list, not nil, when a listing has no items
Problem
Go service-layer list methods built their result with var xs []T and appended per item, so a listing with no items returned a nil slice. A nil slice encodes as JSON null, so consumers that serialize SDK results reported an empty listing as null rather than []. Observed in production through basecamp-mcp-server: list_folders on a project with no folders answered with the bare text null (and no structured result).
Change
- Every listing that decodes a top-level array now starts from an empty slice (
xs := []T{}), across 31 service files (53 sites). Behavior is otherwise unchanged; the populated path appends exactly as before. list_results_empty_test.godrives everyAccountClientservice method that returns a list (a slice, or a pointer to a struct carrying one) against a server that answers[], and asserts the list is non-nil. It enumerates by reflection, so a list method added later is covered without anyone naming it. A floor (50; 70 are exercised today) fails the test if the harness stops reaching them. Againstmainthe test reports 62 nil lists; with this change, none.
Not changed, deliberately
- Nested model fields. Those that distinguish absent from present-but-empty (
Todo.CompletionSubscribers) or omit when empty (Todo.Assignees,omitempty) do so on purpose. - Request/option structs (
BucketIDs,Types,Days, ...): turning a nil into[]there would change request bodies. - Listings that
json.Unmarshalthe body directly (gauges, my assignments, notifications, everything files):[]already decodes to an empty, non-nil slice. - Other SDKs: the nil-vs-empty distinction is Go's; the other runtimes decode
[]to an empty array.
No spec, OpenAPI or generated-code change. make go-check go-check-drift go-check-generated-drift go-check-wrapper-drift passes.
Consumer: basecamp/basecamp-mcp-server will bump to the release carrying this.
Overlap
#991 (fix-pass-api-semantics) touches go/pkg/basecamp/cards.go, checkins.go, lineup.go and projects.go too. Both are rebased on main independently and merge with each other without a textual conflict; the merged tree passes go build, go vet and go test ./.... Whichever lands second needs a small rebase onto the new main.
Summary by cubic
Fixes the Go SDK's list methods returning a nil slice — which encodes as JSON null — when a listing has no items, so empty listings now serialize as [] instead of null. This was observed in production through basecamp-mcp-server, where list_folders on a project with no folders answered bare null.
- Changes 53 sites across 31 service files from
var xs []Ttoxs := []T{}. - Adds a reflective test that drives every
AccountClientservice method returning a list against a server answering[], asserting the result is non-nil; a floor keeps the harness honest as new list methods are added. - Deliberately unchanged: nested model fields that distinguish absent from present-but-empty, and request/option structs.
- No spec, OpenAPI, or generated-code changes.
basecamp/basecamp-mcp-serverwill bump to the release carrying this.