Skip to content

post: a config warning on stdout made a healthy alarm lane look dead - #55

Open
clarkbw wants to merge 1 commit into
mainfrom
post/stdout-warning
Open

post: a config warning on stdout made a healthy alarm lane look dead#55
clarkbw wants to merge 1 commit into
mainfrom
post/stdout-warning

Conversation

@clarkbw

@clarkbw clarkbw commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Post: 2026-08-11-config-warning-on-stdout-…-env-prefix-parsed-commands.md

Draft from blog candidate #16. Date is the draft date (2026-08-11)script/publish.py bumps it at merge.

The arc

Two config credentials moved to secret references. A bare CLI invocation over SSH then failed two differently-visible ways: one field threw a loud error on stderr, the other printed a Config warnings box to stdout — straight into a parsed audit pipeline, so awk '$2=="agent_run"' matched nothing. "Nothing" got read as the soft alarm lane is down rather than your shell is missing an env var: ~40 minutes and one false outage report. The channels compound — 2>/dev/null is a normal reflex that hides the loud failure and leaves the silent one.

Three beats after the finding:

  1. The rule — prefix the env for any command whose output you parse, not just ones that need the secret.
  2. A --selftest that passed while genuinely broken — it grepped for the warning-box title, but the config validate subcommand prints the same condition as a plain text list. Caught only by running the negative control.
  3. The root-cause fix a day later — renaming the secrets file to the dotenv path the CLI auto-loads on every invocation, which deleted the wrapper's entire reason to exist.

Overlap with #50 (post/green-checks) — please read together

#50 uses the selftest as one generalized vignette of six in a theme essay about false-green checks. This post is the full incident with the stdout/stderr channel analysis as its spine; the selftest is one beat inside it, framed as an attempt that failed rather than as a member of a failure class.

They differ enough to both stand, but they are the only two posts in the queue that touch the same selftest. Deliberately not cross-linked with post_url#50 is unpublished, and post_url fails the build against a post that doesn't exist yet. If #50 merges first, this post's Related footer is the natural place to add it (one line, at publish time).

Privacy

Fully generalized from a private-repo source. Neutral tool/wrapper names (gwcli / gw), neutral env vars and config fields, no hostnames, no repo names, no infra paths, reconstructed snippets rather than verbatim file contents. The figures carry no topology beyond "launcher script vs bare CLI".

SEO

Slug/description carry: warnings on stdout corrupt parsed output · stderr vs stdout diagnostics · awk pipeline returns nothing · Missing env var · SecretRefUnavailableError · CLI selftest negative control · env prefix for parsed commands.

Figures (hand-written SVG, 2)

  • two-channels.svg — the stderr/stdout split with the 2>/dev/null cut marked ✕ on the loud lane, under the TL;DR.
  • env-resolution.svg — before/after topology: only the launcher sourced the secrets file vs. the CLI auto-loading it on every invocation.

Verification

  • python3 script/lint-liquid.pypost lint: 35 posts clean
  • bundle exec jekyll build → clean; both <img src="/assets/img/…"> present in _site/<slug>/index.html, both SVGs in _site/assets/img/<slug>/, ${GW_HOOKS_TOKEN} and $secret survive in the rendered code blocks, both post_url links resolve.
  • Figures rendered headless and eyeballed — no overflow, no clipped labels.

Related footer links two published posts: the ntfy delivery-path health check and the OpenClaw-on-a-Pi gateway post.

The full story behind the stdout/stderr channel split: two config credentials
became secret references, a bare CLI invocation then failed two differently-
visible ways — a loud error on stderr and a "Config warnings" box on STDOUT
that corrupted a parsed audit pipeline, so an awk filter returned nothing and
was read as "the alarm lane is down". ~40 min and one false outage report.

Covers the compounding (2>/dev/null hides the loud one, leaves the silent one),
the rule (prefix the env for any command whose output you parse), the selftest
that passed while genuinely broken because it grepped for a warning-box title
the other subcommand never prints, and the root-cause fix a day later that
deleted the wrapper's reason to exist.

Two hand-written SVG figures: the two-channel diagram with the 2>/dev/null cut
marked, and the before/after env-resolution topology.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HaMaRk9UijJQMDX48EVvTk
@clarkbw clarkbw added the publish Approved to publish via the drip-publisher routine label Aug 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

publish Approved to publish via the drip-publisher routine

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant