Blogs Engineering

Stopping Health-Check Settings From Leaking Between Targets in Newt

Artigo em inglês. After a reconnect, Newt could give one target another target's expected status code. The merged fix decodes each payload into a fresh struct.

Gofosrl/newtnetworkingJSON decodinghealth checks

A subtle encoding/json behavior, diagnosed in public by the reporter, and fixed with a fresh struct per payload.

Resposta curta

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!"

Perguntas frequentes

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.

O objetivo

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.

Executar o ciclo

Transforme sinais de produção em correções revisadas.

Comece um teste grátis e veja como a Prilog conecta incidentes reais a pull requests no nível do código.