docs/solutions/logic-errors/curl-failure-sentinel-doubled-by-echo-fallback.md
.github/workflows/test-linux-app.yml runs a scheduled "Desktop Canary
(Linux)" job (PR #5915, part of epic #5902) that builds the packaged Tauri
desktop app and asserts, against a real launched instance, that its local
sidecar (the bundled Node API server on 127.0.0.1:46123) answers HTTP
requests and that the rendered window is non-blank. The sidecar check polls
the endpoint in a loop and captures the HTTP status via
curl -s -o /dev/null -w '%{http_code}'. As originally written on the PR
branch, the capture line was:
CODE=$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 \
"http://127.0.0.1:46123/api/local-traffic-log" || echo 000)
if [ "$CODE" != "000" ]; then
SIDECAR_STATUS=ready
fi
The intent was standard defensive shell: "if curl fails outright, fall back
to a 000 sentinel so the readiness check has something to compare
against." That intent is exactly backwards for this particular curl
invocation: the fallback made the gate permanently unable to fire — a dead
sidecar would have been reported as ready on every scheduled run. The probe
was introduced and caught within the same PR (#5915), so the defect never
reached a live CI run.
SIDECAR_STATUS=ready vs unreachable)
could not distinguish a live sidecar from a completely dead one — a
closed port on 46123 still drove the loop into the ready branch.ready branch for a real 3-digit status code, so nothing looks
wrong. The defect only surfaces when probed against a dead port, or when
the shell semantics are traced closely..github/workflows/test-linux-app.yml). A canary that cannot fail on its
own target defect provides zero signal while looking fully instrumented.Reading the line in isolation looks correct: curl ... || echo 000 reads
as "produce 000 if curl fails," and [ "$CODE" != "000" ] reads as "not
000 means it responded." Nothing about || echo 000 is malformed shell —
each half is individually idiomatic. The defect is only visible by tracing
what curl -w '%{http_code}' actually writes to stdout on a connection
failure, not by reading the conditional.
The bug was not caught by:
It was caught by review — by three independent reviewers converging on the identical line before merge: a correctness-focused pass, an in-process adversarial pass, and a separately-trained cross-model (Codex) adversarial pass. The cross-model agreement, with no shared context between reviewers, was the strongest signal the finding was real.
Fixed on PR #5915 by dropping the || echo 000 fallback entirely, since
curl -w '%{http_code}' already emits 000 on its own when the connection
fails — the fallback was concatenating a second, redundant sentinel onto
output curl had already produced:
# NOTE: no `|| echo 000` fallback on the curl — curl already prints
# 000 via -w on connection failure, and appending a second sentinel
# would make CODE "000000", which passes the != "000" check and
# reports a dead sidecar as ready.
SIDECAR_STATUS=unreachable
for i in $(seq 1 12); do
CODE=$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 \
"http://127.0.0.1:46123/api/local-traffic-log")
if [ "$CODE" != "000" ]; then
SIDECAR_STATUS=ready
echo "sidecar responded with HTTP $CODE on attempt $i"
break
fi
sleep 5
done
The NOTE comment sits directly above the probe
(.github/workflows/test-linux-app.yml:125-128 as of PR #5915) so a future
edit doesn't reintroduce the same "defensive" fallback for the same reason
it was added the first time.
Empirical repro, runnable standalone against any closed local port:
CODE=$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 "http://127.0.0.1:1/x" || echo 000); echo "[$CODE]"
# buggy form -> [000000] (the [ "$CODE" != "000" ] gate would report "ready")
CODE=$(curl -s -o /dev/null -w '%{http_code}' --max-time 3 "http://127.0.0.1:1/x"); echo "[$CODE]"
# fixed form -> [000] (correctly compares equal to "000", gate reports "unreachable")
Both forms were run during the session: the buggy form printed [000000]
("WOULD REPORT READY"), the fixed form printed [000] and was correctly
classified unreachable.
curl -w '%{http_code}' is not silent on connection failure — it still
writes the format string to stdout, substituting 000 for the status code,
and it also exits non-zero. Both halves of $(cmd || fallback) therefore
fire on that failure path: curl prints 000 to the command-substitution's
stdout, and its non-zero exit trips the ||, which prints a second 000
right after it. Command substitution captures everything written to stdout
in the subshell, so the captured value is the literal string 000000 — and
[ "000000" != "000" ] is true, taking the branch reserved for a real
status code even though nothing answered.
Removing the fallback fixes it because curl's own 000 sentinel is already
the complete, correct signal for "no response" on this invocation. The
general shape: $(cmd ... || echo <sentinel>) is safe only when cmd is
silent on that failure path. The moment the command's own failure path
already writes to stdout, an ||-appended echo doesn't replace that
output — it concatenates onto it, and every subsequent string comparison
against the bare sentinel silently stops working, without a syntax error or
visible failure anywhere in the diff.
Never add || echo <sentinel> inside a command substitution without
first checking what the command already prints on that exact failure
path. If the command emits its own sentinel on failure — curl -w is
the canonical case, but any tool with an "always print a status/format
string" flag has the same shape — the fallback concatenates with it
instead of replacing it. The two safe alternatives: rely on the
command's own sentinel and drop the fallback (what was done here), or
branch on the exit code directly (if CODE=$(curl ...); then). Never
combine an output-sentinel approach with an appended-fallback approach
on the same call.
Mutation-test every detection layer before trusting it — reading a
guard only tells you what it intends, not what it catches. This is a
fifth mechanism for the repo's Vacuous Guard concept: the guard's
comparison value itself gets corrupted (two failure sentinels
concatenating), rather than its input shrinking to nothing. The
discipline in
docs/solutions/conventions/verify-the-verifier-mutation-test-every-detection-layer.md
is what proves such a guard: deliberately break the exact thing it
exists to catch and watch it turn red. In this session that was applied
to the fixed probe (dead port → red) and to the sibling detection layers
in the same PR — corrupt capability JSON → config-parse gate red,
deleted handler bundle → bundle assertion red, fixture logs through each
canary check branch → each red.
Weight cross-model / cross-persona review convergence. Three independent reviewers landing on one line, with no shared context, is evidence the finding is real that is distinct from any single reviewer's confidence — use that convergence to prioritize which findings to act on first.
docs/solutions/conventions/verify-the-verifier-mutation-test-every-detection-layer.md
— the mutation-proof convention this doc's Prevention #2 applies (same
meta-rule, different mechanism; cross-referenced, not merged)docs/solutions/best-practices/checks-must-fail-closed-when-they-lose-their-target.md
— the fail-loud direction the canary's warn→hard-fail conversion followsCONCEPTS.md — Vacuous Guard, Mutation Proof, Surviving Mutant