#6021·beads

bd update --notes "" wipes the notes field at exit 0 with a success receipt; --allow-empty-description has no --allow-empty-notes counterpart

Author: olgasafonovaCreated Aug 26, 2026Updated Sep 16, 2026

TL;DR

bd update <id> --notes "" erases the entire notes field, prints ✓ Updated issue: ..., and exits 0. There is no flag to gate it. bd update already ships --allow-empty-description for exactly this hazard on the description field, so the guard concept exists in the CLI and is simply not wired to notes.

The common way to hit this is not typing --notes "" on purpose. It is a command substitution that collapses to the empty string:

bash
bd update "$ID" --notes "$(cat merged.txt)"   # merge script died before writing merged.txt

Nothing about the output distinguishes that from a successful write.

Environment

Item Value
bd version 1.0.5 (Homebrew)
OS macOS (darwin 25.5.0), Apple silicon
Engine embedded Dolt (in-process, no server)

Reproduction

On a throwaway issue, four cases. Only the last is the bug; the other three are included because the contrast is the argument.

bash
ID=$(bd create "throwaway" --json | ...)   # any issue

bd update "$ID" --notes "SECTION_ALPHA: first section."
bd show "$ID" | grep SECTION_ALPHA          # present

bd update "$ID" --append-notes "SECTION_BETA: second section."
bd show "$ID" | grep -c 'SECTION_ALPHA\|SECTION_BETA'   # 2 — appends correctly

rm -f /tmp/definitely-missing.txt
bd update "$ID" --append-notes "$(cat /tmp/definitely-missing.txt 2>/dev/null)"
bd show "$ID" | grep -c 'SECTION_ALPHA\|SECTION_BETA'   # 2 — safe no-op, good

bd update "$ID" --notes "$(cat /tmp/definitely-missing.txt 2>/dev/null)"
bd show "$ID" | grep -c 'SECTION_ALPHA\|SECTION_BETA'   # 0 — WIPED

Observed on the last call:

✓ Updated issue: <id> — throwaway
exit 0
Command Content Result
--append-notes text appends with newline separator, both survive
--append-notes "" from a dead $(cat) safe no-op, nothing lost, exit 0
--notes text replaces, prior content gone (documented behaviour)
--notes "" from a dead $(cat) wipes, ✓ Updated, exit 0

--append-notes already does the right thing on an empty payload. That is the strongest evidence the empty case is understood somewhere in the codebase and just is not applied on the --notes path.

Why this is worth a flag rather than "don't do that"

Replacement on --notes is correct and expected. The bug is that the empty replacement is indistinguishable from success, so the failure is silent and the data is unrecoverable from the CLI. In our own working notes this failure has twelve dated occurrences across three distinct root causes, all of them upstream of the --notes call rather than in it:

  1. a command substitution collapsing to the empty string when its input file was never written;
  2. bd show <id> --json returning a JSON list rather than an object, so a naive json.load(...).get('notes') throws and the extraction writes a zero-byte file;
  3. bd invoked from outside the beads repo writing an empty --json payload at exit 0, indistinguishable from an empty notes field.

Each of those produces the same thing at the call site: an empty string. Any of them plus --notes is a silent wipe. The largest single loss we recorded was 5,400 characters, caught only because a per-section grep ran afterwards.

Proposed fix

Mirror the existing description behaviour:

  • bd update <id> --notes "" (or a substitution that resolves to empty) exits non-zero with a message naming the current notes length and pointing at the override.
  • Add --allow-empty-notes to perform the erase deliberately.
  • Optionally extend the same guard to --notes-file / stdin input, which is where --allow-empty-description already applies.

A softer variant, if a hard refusal is considered breaking: keep exit 0 but make the receipt honest, e.g. ✓ Updated issue: <id> (notes cleared, 5400 chars removed). Most of the damage here comes from a success string that reads identically whether 5,400 characters survived or vanished.

Related

  • #3964 — --append-notes silently drops writes in rapid succession. Same silent-loss-behind-a-success-receipt shape on the sibling flag. Relevant here because --append-notes is otherwise the correct answer to most of what people reach for --notes to do.
  • #3102 — --notes-file / --append-notes-file. If those land, the empty-payload guard should cover them from the start; a file that exists but is zero bytes is the same hazard with a different surface.