A subtle encoding/json behavior, diagnosed in public by the reporter, and fixed with a fresh struct per payload.
짧은 답변
When two Newt targets shared a backend and a websocket reconnect re-sent the configuration, one target could inherit the other's expected status code and be reported unhealthy forever. Newt decoded each registration payload into its existing state, and Go's encoding/json can reuse existing slice elements while decoding an array. The fix decodes into a fresh WgData and swaps it in only on success. It was merged as PR #462 and closed issue #411.
The problem
Newt is the tunneling client for the Pangolin project. Issue #411 described two targets on the same host:port, one expecting 200 and the other 401. After a reconnect, the first target logged unexpected status code: 200 (expected: 401) on every check. A restart cleared it until the next reconnect.
Root cause
The reporter traced it in the issue thread. handleConnect decoded the payload into the persistent n.wgData:
json.Unmarshal(jsonData, &n.wgData)
The relevant behavior is reuse of existing slice elements during decoding: a JSON null does not reset a non-pointer int. If targets arrived in a different order, or with a null status, old values survived into the wrong entries. The reporter linked a community pull request, #413, proposing the same approach. The supplied research records that PR as still open on Nov 3, 2026.
The fix
func (n *Newt) applyWgData(jsonData []byte) error {
var wgData WgData
if err := json.Unmarshal(jsonData, &wgData); err != nil {
return err
}
n.wgData = wgData
return nil
}
Three tests cover a reordered health-check payload with a null status, stale fields being cleared, and existing data being kept when the payload is malformed.
By the numbers
- Repository: fosrl/newt, 921 stars (Nov 3, 2026), Go, AGPL-3.0
- Files changed: 2 (1 source file, 1 test file)
- Lines: +77 / -1
- Commits: 1
- Opened: Oct 6, 2026. Merged: Oct 12, 2026. Time to merge: 6 days, 2 hours
- Review: approved and merged by a maintainer
How the review went
A maintainer approved, merged and replied "Thanks!"
FAQ
Why not reset fields individually?
Decoding into a fresh struct removes the whole class of positional-merge and null-handling problems at once, and the error path leaves the previous state untouched.
Where can I read more?
See the pull request, the original issue, and the earlier community PR #413.
목표
The hard part here was the diagnosis, and it happened in public, in the issue. The merged change is small because the cause was understood.