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 401 → same 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).
Describe the Bug
When two resources share the same backend
host:portbut have different health-check Expected Status Codes, the server sends conflictinghcStatusvalues 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
GET /manifest.json, Expected Status 200 → backend10.0.0.10:8123GET /api/mcp, Expected Status 401 → same backend10.0.0.10:8123Clean 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:
Immediately after, Newt logs every interval:
So:
newt/syncpayload carries target A →hcStatus: 200(correct)newt/wg/connectregistration 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/
getConfigpath) keyed or de-duplicated by backendhost: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 thesyncand registration payloads should agree.Environment
Workaround
Restart Newt for a clean reload — temporary; the collision returns on the next reconnect. Or avoid two resources sharing an identical backend
host:portfor health checks (disable the health check on one, or differentiate the backends).