What happened
During CLI-042's post-deploy verification on msi, hive.service crash-looped. Its journal said, 34 times:
dotf[112172]: Error: bw resolve nan-api-key/api-key: bw item not found:
bw get item "nan-api-key": exec: "bw": executable file not found in $PATH
The same command run from an interactive shell said something different:
Error: bw resolve nan-api-key/api-key: bitwarden vault is locked:
no bw serve daemon is running — run `dotf secrets unlock` (not `bw unlock`, which
only prints a session key to your shell and leaves this command's vault locked)
Same failure, same machine, same second. The only difference is the caller's PATH: bw lives in an nvm-managed directory (~/.nvm/versions/node/<ver>/bin) that a systemd --user unit does not inherit.
Why the difference matters
dotf secrets resolves over the bw serve daemon on loopback and falls back to the bw CLI. When the vault is locked, the daemon is absent, so resolution falls through to the CLI — and there the two contexts diverge:
- interactive:
bw is found, reports vault is locked, names the fix (dotf secrets unlock)
- systemd:
bw is not found, reports executable not found, names no fix
Only the first message is actionable. The second describes the fallback's own incidental problem and points at PATH, which is not the cause and whose repair would change nothing — once the vault is unlocked the daemon serves and the CLI is never consulted. Confirmed by consequence: after dotf secrets unlock, hive converged on the next retry with the PATH untouched.
ActiveState=active SubState=running Result=success
NRestarts=34 -> 34 (unchanged across a 40s window; RestartSec=30)
The misleading variant is the one that appears where no human is watching. A daemon's journal is read during an incident, by someone who does not yet know why it is down. This one sends them to fix a PATH that is not broken.
Measured cost: it produced a written finding asserting the drop-in's convergence design was defeated and would never converge, and the fix that finding implied — rendering the nvm bin directory into the unit's PATH at setup — would have added machinery to work around a problem that does not exist. Caught only by running the discriminating test instead of acting on the reconstruction.
What a fix looks like
The failure the user must act on is "the vault is locked", and dotf knows that before it ever tries the CLI — the serve daemon's absence is the thing it detected first. The CLI's exec error should not be able to displace it.
- Report the daemon-absent cause as primary, and demote the fallback's error to a detail. When the serve daemon is unreachable, say so and name
dotf secrets unlock, regardless of how the fallback then fails.
- Treat "bw not on PATH" as its own diagnosable condition rather than an item-resolution error — the current text (
bw item not found: ... executable file not found) claims an item lookup failed when no lookup happened.
- Optionally, have
dotf doctor note that bw is absent from the systemd user PATH, so it is known before an incident rather than during one.
Acceptance
Related: CLI-024 (bw serve backend), ADR-028, #1230 (the drop-in whose journal surfaced this).
What happened
During CLI-042's post-deploy verification on
msi,hive.servicecrash-looped. Its journal said, 34 times:The same command run from an interactive shell said something different:
Same failure, same machine, same second. The only difference is the caller's
PATH:bwlives in an nvm-managed directory (~/.nvm/versions/node/<ver>/bin) that a systemd--userunit does not inherit.Why the difference matters
dotf secretsresolves over the bw serve daemon on loopback and falls back to thebwCLI. When the vault is locked, the daemon is absent, so resolution falls through to the CLI — and there the two contexts diverge:bwis found, reports vault is locked, names the fix (dotf secrets unlock)bwis not found, reports executable not found, names no fixOnly the first message is actionable. The second describes the fallback's own incidental problem and points at
PATH, which is not the cause and whose repair would change nothing — once the vault is unlocked the daemon serves and the CLI is never consulted. Confirmed by consequence: afterdotf secrets unlock, hive converged on the next retry with the PATH untouched.The misleading variant is the one that appears where no human is watching. A daemon's journal is read during an incident, by someone who does not yet know why it is down. This one sends them to fix a
PATHthat is not broken.Measured cost: it produced a written finding asserting the drop-in's convergence design was defeated and would never converge, and the fix that finding implied — rendering the nvm bin directory into the unit's PATH at setup — would have added machinery to work around a problem that does not exist. Caught only by running the discriminating test instead of acting on the reconstruction.
What a fix looks like
The failure the user must act on is "the vault is locked", and
dotfknows that before it ever tries the CLI — the serve daemon's absence is the thing it detected first. The CLI'sexecerror should not be able to displace it.dotf secrets unlock, regardless of how the fallback then fails.bw item not found: ... executable file not found) claims an item lookup failed when no lookup happened.dotf doctornote thatbwis absent from the systemd user PATH, so it is known before an incident rather than during one.Acceptance
bwoffPATH, the error names the locked vault anddotf secrets unlockRelated: CLI-024 (bw serve backend), ADR-028, #1230 (the drop-in whose journal surfaced this).