Skip to content

Health check Expected Status Code collides between two resources sharing the same backend host:port on reconnect #3444

Description

@gravis

Describe the Bug

When two resources share the same backend host:port but have different health-check Expected Status Codes, the server sends conflicting hcStatus values for the same target across two messages on a reconnect, and the resource ends up permanently Unhealthy even though it responds correctly.

Newt applies each health-check config it receives faithfully, keyed by target ID — so this is a server-side payload inconsistency, not a Newt bug. (Originally filed against Newt as fosrl/newt#411; traced to the server.)

Setup that triggers it

  • Resource A — health check GET /manifest.json, Expected Status 200 → backend 10.0.0.10:8123
  • Resource B — health check GET /api/mcp, Expected Status 401same backend 10.0.0.10:8123

Clean boot is fine. After a Newt websocket reconnect (server restart, DNS blip, or brief network drop), Resource A goes Unhealthy and stays that way until Newt is restarted.

Root cause (from Newt logs on reconnect)

On reconnect the server sends two messages that disagree on target A's expected code:

Websocket connected
Received sync message
Syncing health check targets: 2 desired targets
Sync complete: removed 0, added 0, updated 2 targets
Target B initial status: healthy
Target A initial status: healthy          <-- sync payload: target A expects 200 (correct)
...
Replacing existing target with ID A       <-- newt/wg/connect (registration) payload
Replacing existing target with ID B
Target B initial status: healthy
Target A initial status: unhealthy        <-- registration payload: target A expects 401 (wrong)

Immediately after, Newt logs every interval:

Target A: unexpected status code: 200 (expected: 401)

So:

  • the newt/sync payload carries target A → hcStatus: 200 (correct)
  • the newt/wg/connect registration payload carries target A → hcStatus: 401 (wrong — it's Resource B's expected code)

The registration message arrives second and overwrites the correct value. Target A (/manifest.json) then fails forever because it's checked against 401.

This strongly suggests the server builds the health-check target list (at least in the registration/getConfig path) keyed or de-duplicated by backend host:port, so when two resources share a backend, one resource inherits the other's expected status code.

Expected Behavior

Each health-check target should carry its own configured Expected Status Code regardless of whether another resource shares the same backend host:port, and the sync and registration payloads should agree.

Environment

  • Pangolin Version: 1.20.0 (also observed on 1.19.4)
  • Newt Version: 1.14.0

Workaround

Restart Newt for a clean reload — temporary; the collision returns on the next reconnect. Or avoid two resources sharing an identical backend host:port for health checks (disable the health check on one, or differentiate the backends).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions