Skip to content

Go: return an empty list, not nil, when a listing has no items - #990

Merged
jeremy merged 1 commit into
mainfrom
list-results-empty-not-nil
Oct 15, 2026
Merged

jeremy merged 1 commit into
mainfrom
list-results-empty-not-nil

Conversation

@jeremy

@jeremy jeremy commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

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.go drives every AccountClient service 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. Against main the 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.Unmarshal the 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 []T to xs := []T{}.
  • Adds a reflective test that drives every AccountClient service 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-server will bump to the release carrying this.

Written for commit 7947e39. Summary will update on new commits.

View guided diff Turn on auto-fix

Copilot AI balanced review requested due to automatic review settings October 15, 2026 08:50
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-15T15:40:42.145877Z 7947e39 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Copilot AI previously approved these changes Oct 15, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approved

The narrow initialization changes correct empty-result serialization without changing populated-result processing, and no blocking issues remain.

0 open findings

What changed in this PR

Fixes Go SDK empty-list serialization so consumers receive [] instead of null.

Changes:

  • Initializes list results as non-nil empty slices across 31 service files.
  • Adds reflective regression coverage for empty list results.
  • Leaves request bodies, nested model fields, and generated code unchanged.

[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

File Description
go/​pkg/​basecamp/​webhooks.go Initializes empty webhook lists.
go/​pkg/​basecamp/​vaults.go Initializes empty vault, document, upload, and version lists.
go/​pkg/​basecamp/​todos.go Initializes empty todo lists.
go/​pkg/​basecamp/​todolists.go Initializes empty todolist lists.
go/​pkg/​basecamp/​todolist_groups.go Initializes empty group lists.
go/​pkg/​basecamp/​timesheet.go Initializes empty report entries.
go/​pkg/​basecamp/​timeline.go Initializes empty timeline events.
go/​pkg/​basecamp/​templates.go Initializes empty template lists.
go/​pkg/​basecamp/​subtasks.go Initializes empty subtask lists.
go/​pkg/​basecamp/​search.go Initializes empty search results.
go/​pkg/​basecamp/​schedules.go Initializes empty schedule entries.
go/​pkg/​basecamp/​recordings.go Initializes empty recording lists.
go/​pkg/​basecamp/​projects.go Initializes empty project lists.
go/​pkg/​basecamp/​people.go Initializes empty people lists.
go/​pkg/​basecamp/​messages.go Initializes empty message lists.
go/​pkg/​basecamp/​message_types.go Initializes empty message-type lists.
go/​pkg/​basecamp/​list_results_empty_test.go Adds reflective non-nil list checks and a coverage floor.
go/​pkg/​basecamp/​lineup.go Initializes empty marker lists.
go/​pkg/​basecamp/​forwards.go Initializes empty forward and reply lists.
go/​pkg/​basecamp/​folders.go Initializes empty folder lists.
go/​pkg/​basecamp/​everything.go Initializes empty aggregate listing results.
go/​pkg/​basecamp/​events.go Initializes empty event lists.
go/​pkg/​basecamp/​drafts.go Initializes empty draft lists.
go/​pkg/​basecamp/​comments.go Initializes empty comment lists.
go/​pkg/​basecamp/​client_replies.go Initializes empty client reply lists.
go/​pkg/​basecamp/​client_correspondences.go Initializes empty correspondence lists.
go/​pkg/​basecamp/​client_approvals.go Initializes empty approval lists.
go/​pkg/​basecamp/​checkins.go Initializes empty question, answer, people, and reminder lists.
go/​pkg/​basecamp/​cards.go Initializes empty card lists.
go/​pkg/​basecamp/​campfires.go Initializes empty Campfire, line, upload, and chatbot lists.
go/​pkg/​basecamp/​boosts.go Initializes empty boost lists.
go/​pkg/​basecamp/​bookmarks.go Initializes empty bookmark lists.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@jeremy
jeremy requested a balanced review from Copilot October 15, 2026 08:58
Copilot AI dismissed their stale review, a newer Copilot review was requested October 15, 2026 08:58
Copilot AI previously approved these changes Oct 15, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approved

The localized initializations preserve existing processing, and no blocking issues were identified in the changes or regression harness.

0 open findings

🧠 Review effort: Balanced

@jeremy

jeremy commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

Open for a decision: four checks fail on d7c8053, none caused by this diff:

  • Go Vulnerability Check: govulncheck flags GO-2026-6617 and GO-2026-6613 in net/http on CI's go1.26.8 (fixed in go1.26.9). The scheduled main run on 490e10d failed the same way.
  • cargo deny and Rust stable: RUSTSEC-2026-0346 in rustls 0.23.45 (fixed in 0.23.46).
  • Conformance Tests: fails only because test-rust failed.

Fixing them means bumping the CI Go toolchain and rustls, which is separate dependency work outside this Go-only change. Someone needs to decide whether to land that first or merge over it.
Copilot approved d7c8053 and Codex reported on it with no findings.

A list method that found nothing returned a nil slice, which encodes as
JSON null. A consumer that serializes the result (the MCP server, the CLI's
JSON output) reported an empty folder listing as null rather than [].

Each listing now starts from an empty slice. A reflective test drives every
AccountClient service method that returns a list against a server answering
[] and asserts the list is non-nil, so a listing added later is covered
without being named; a floor fails the test if the harness stops reaching
them. Nested model fields are untouched: those that distinguish absent from
empty (completion_subscribers) or omit when empty (assignees) do so on
purpose.
@jeremy
jeremy force-pushed the list-results-empty-not-nil branch from d7c8053 to 7947e39 Compare October 15, 2026 15:38
@jeremy
jeremy requested a balanced review from Copilot October 15, 2026 15:38
@jeremy

jeremy commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

@codex review

Copilot AI dismissed their stale review, a newer Copilot review was requested October 15, 2026 15:38
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 7947e392b0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approved

The targeted slice initializations preserve existing control flow, and static review found no blocking issues in the changes or regression test.

0 open findings

🧠 Review effort: Balanced

@jeremy

jeremy commented Oct 15, 2026

Copy link
Copy Markdown
Member Author

Nothing open on 7947e39.

@jeremy
jeremy merged commit d3a4f15 into main Oct 15, 2026
51 checks passed
@jeremy
jeremy deleted the list-results-empty-not-nil branch October 15, 2026 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants