Z2M: quote the !secret ref (fixes 'unknown scalar tag', Z2M won't start) #370

Merged
k.renner merged 1 commits from claude-bot/zha-to-zigbee2mqtt into master 2026-07-23 11:28:22 +00:00
Member

Third and final config fix — verified against Z2M's own validator this time, not guessed.

Root cause (definitive)

Z2M refused to start with a generic "configuration is invalid" message. Running Z2M's own settings.validate() in the image gave the real error:

THROW: unknown scalar tag !<!secret> (17:13)
   password: !secret mqtt_password

Z2M 2.x does not register a !secret YAML tag. Its secret mechanism is parseValueRef — a regex !<file> <key> run against the string value of mqtt.password (etc.) in read(). So the reference must be a quoted string:

password: "!secret mqtt_password"

Changes

  • Quote the !secret ref in the ConfigMap; restore the init container rendering /app/data/secret.yaml (the interim ZIGBEE2MQTT_CONFIG_* env-override was reverted — it's only applied in write()/onboarding, not on a plain read, so it left mqtt.password empty; confirmed in-image).

Verified end-to-end (real rendered ConfigMap, in the running image)

VALIDATE_ERRORS: []
mqtt.password: <resolved from secret.yaml>
devices: 31

Cutover state

  • ✅ OpenBao backup, ExternalSecret synced
  • ✅ ZHA re-enabled — your Zigbee is working now (I rolled it back after the earlier failed starts; Z2M never touched the coordinator)
  • ⏳ After merge: I clear the stale PVC config once, Z2M starts & validates against the live coordinator (ZHA still holds it, so it'll reach connect-stage — proving config good), then the final flip (disable ZHA → Z2M restores).

🤖 Generated with Claude Code

Third and final config fix — **verified against Z2M's own validator this time**, not guessed. ## Root cause (definitive) Z2M refused to start with a generic "configuration is invalid" message. Running Z2M's own `settings.validate()` in the image gave the real error: ``` THROW: unknown scalar tag !<!secret> (17:13) password: !secret mqtt_password ``` Z2M 2.x does **not** register a `!secret` YAML *tag*. Its secret mechanism is `parseValueRef` — a regex `!<file> <key>` run against the **string value** of `mqtt.password` (etc.) in `read()`. So the reference must be a **quoted string**: ```yaml password: "!secret mqtt_password" ``` ## Changes - Quote the `!secret` ref in the ConfigMap; restore the init container rendering `/app/data/secret.yaml` (the interim `ZIGBEE2MQTT_CONFIG_*` env-override was reverted — it's only applied in `write()`/onboarding, **not** on a plain read, so it left `mqtt.password` empty; confirmed in-image). ## Verified end-to-end (real rendered ConfigMap, in the running image) ``` VALIDATE_ERRORS: [] mqtt.password: <resolved from secret.yaml> devices: 31 ``` ## Cutover state - ✅ OpenBao backup, ExternalSecret synced - ✅ **ZHA re-enabled** — your Zigbee is working now (I rolled it back after the earlier failed starts; Z2M never touched the coordinator) - ⏳ After merge: I clear the stale PVC config once, Z2M starts & validates against the live coordinator (ZHA still holds it, so it'll reach connect-stage — proving config good), then the final flip (disable ZHA → Z2M restores). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-bot added 1 commit 2026-07-23 11:25:54 +00:00
Root cause of Z2M refusing to start, verified against Z2M's own validator: the
MQTT password was written as a bare YAML tag `password: !secret mqtt_password`,
and Z2M 2.x does NOT register a `!secret` YAML tag — js-yaml throws
"unknown scalar tag !<!secret>", which surfaces as the generic
"configuration is invalid (use yaml-validator)" message.

Z2M's secret mechanism is parseValueRef (settings.ts read()): it matches the
regex `!<file> <key>` against the STRING VALUE of mqtt.user/password/server,
advanced.network_key, frontend.auth_token. So the reference must be a QUOTED
string, not a tag:

    password: "!secret mqtt_password"

Reverted the interim env-override attempt: ZIGBEE2MQTT_CONFIG_* is applied only
in write()/onboarding, not on a plain read of an existing config, so it did NOT
set mqtt.password (confirmed: mqtt.password was empty). The quoted-!secret +
init-rendered /app/data/secret.yaml path is the one Z2M actually resolves on
read.

Verified end-to-end on the REAL rendered ConfigMap via `settings.validate()` in
the running image: VALIDATE_ERRORS: [], mqtt.password resolves from secret.yaml,
31 devices parsed.

Post-merge: the PVC still holds the pre-fix seeded configuration.yaml (init only
seeds when absent), so it must be cleared once so it re-seeds from this fixed
ConfigMap.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
k.renner merged commit 8ebd870459 into master 2026-07-23 11:28:22 +00:00
Sign in to join this conversation.