Skip to content

fix: decode Job.Signal as a signal name, not an int - #354

Merged
wolfeidau merged 1 commit into
mainfrom
fix/job-signal-string
Aug 2, 2026
Merged

fix: decode Job.Signal as a signal name, not an int#354
wolfeidau merged 1 commit into
mainfrom
fix/job-signal-string

Conversation

@jeremybumsted

@jeremybumsted jeremybumsted commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Job listing and job fetching fail outright for any job killed by a signal. The REST API returns the symbolic signal name — the agent sets it via process.SignalString — but go-buildkite v5.7.0, which we pin, typed Job.Signal as *int:

json: cannot unmarshal string into Go struct field Job.items.signal of type int

The SDK discards the entire response on a decode error, so one signal-killed job takes out every other job in the build. Timed-out jobs get SIGKILL'd, which means any build with a mass timeout — a 90-shard suite hitting its limit, say — returns nothing at all rather than 89 usable jobs.

  • bump go-buildkite/v5 v5.7.0 → v5.10.0; the field was corrected to string upstream in v5.8.0 (buildkite/go-buildkite#350)
  • follow the type through JobDetail.Signal, which mirrored the same wrong *int and passed it straight through from detailJob
  • add TestJobSignalDecodesFromWire, covering a signal name, an explicit null, and an absent field

Behaviour change

JobDetail.signal in list_jobs / get_job output changes from a number to a string — 9 becomes "SIGKILL". This is the field's real wire type; it has never successfully returned a number, since any response that would have populated it failed to decode first. Nothing downstream can be relying on the old shape.

Why our tests missed this

Every existing job test constructs buildkite.Job values directly through MockJobsClient, so none of them exercise JSON decoding — the mock hands back an already-built struct and the wire format is never parsed. A *int field looks fine under that setup right up until it meets a real API response.

The added test starts from wire bytes instead. Worth noting it only closes the hole for this one field: the same blind spot covers the whole mocked tool surface, so wire-type drift in builds, artifacts, or annotations would land exactly the same way. Broader wire-level fixtures are worth doing separately.

Verification

  • one compile error across the 76-commit SDK jump, the expected Signal one; the backoffroko dependency swap didn't move go.sum
  • go build ./... clean, golangci-lint run ./... 0 issues, full suite green
  • confirmed the new test fails against the broken version rather than merely passing: its payload unmarshalled with v5.7.0 reproduces the reported error verbatim

Disclosures/Credits

  • Claude generated the code changes and descriptions, reviewed by moi.

The REST API returns the symbolic signal name ("SIGKILL", "SIGTERM") —
the agent sets it via process.SignalString — but go-buildkite v5.7.0
typed Job.Signal as *int. Any job killed by a signal therefore failed to
decode:

  json: cannot unmarshal string into Go struct field Job.items.signal of type int

Because the SDK discards the whole response on a decode error, a single
signal-killed job took out every other job in the build. Timed-out jobs
get SIGKILL'd, so any build with a mass timeout hit this.

go-buildkite fixed the field to string in v5.8.0 (75e7abd). Bump to
v5.10.0 and follow the type through JobDetail, which mirrored the same
wrong *int.

Note JobDetail.signal changes from number to string in tool output.

The existing tests build buildkite.Job values directly through the mock,
so they never exercised JSON decoding and could not have caught this.
The added test starts from wire bytes instead, covering a signal name,
an explicit null, and an absent field.
Copilot AI review requested due to automatic review settings August 1, 2026 00:20
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedgolang/​github.com/​buildkite/​go-buildkite/​v5@​v5.7.0 ⏵ v5.10.071100100100100

View full report

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Fixes job listing/fetching failures when Buildkite returns a symbolic signal name (e.g., "SIGKILL") by aligning this repo and the pinned go-buildkite SDK with the API’s real wire type for signal.

Changes:

  • Bump github.com/buildkite/go-buildkite/v5 from v5.7.0 to v5.10.0 (includes upstream fix for Job.Signal decoding).
  • Update this repo’s JobDetail.Signal to string and keep it as a passthrough from the SDK job model.
  • Add a wire-level regression test (TestJobSignalDecodesFromWire) to ensure string/null/absent signal values decode and propagate through list_jobs detailed output.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated no comments.

File Description
pkg/buildkite/jobs.go Updates JobDetail.Signal to string and passes through the SDK’s decoded signal name.
pkg/buildkite/jobs_test.go Updates existing expectations and adds a wire-level JSON decoding regression test for job signals.
go.mod Bumps go-buildkite/v5 dependency to v5.10.0.
go.sum Updates module sums for the new go-buildkite/v5 version.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@buildsworth-bk-app buildsworth-bk-app Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

signal changes type in the public list_jobs / get_job output contract, so this needs human review; I didn't find any implementation issues to call out.

Want to dig deeper?

Paste this into your agent to explore the findings from this review's Buildkite build:

Download the buildsworth logs from build 10687, then answer my questions about the findings.

Install the reading-buildsworth-logs skill to run this.

About buildsworth

Model: gpt-5.6-sol with xhigh thinking.

How to request a review: Comment @buildsworth-bk review on the PR, or request buildsworth-bk as a reviewer.

Risk labels (how buildsworth classifies risk) — buildsworth classifies risk itself from the diff. To let it approve, grant L2 approval by mentioning @buildsworth-bk (see L2 approval grant):

  • L1 — Low risk (dep bumps, docs/copy, lockfiles, small presentational fixes). buildsworth may approve by default.
  • L2 — Standard risk (new UI, additive API fields, refactors). Approved only with an L2 grant; otherwise comment-only.
  • L3 — High risk (auth, migrations, payments, secrets, perf-critical paths). Human review always required.

@wolfeidau
wolfeidau merged commit c8fb3e7 into main Aug 2, 2026
4 of 5 checks passed
@wolfeidau
wolfeidau deleted the fix/job-signal-string branch August 2, 2026 23:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants