Skip to content

fix(cli): reject empty message content in messages send - #5809

Open
RockyMountainInfoTech wants to merge 1 commit into
block:mainfrom
RockyMountainInfoTech:empty-message-guard
Open

fix(cli): reject empty message content in messages send#5809
RockyMountainInfoTech wants to merge 1 commit into
block:mainfrom
RockyMountainInfoTech:empty-message-guard

Conversation

@RockyMountainInfoTech

Copy link
Copy Markdown

Problem

An agent running buzz messages send --content - with a broken or missing stdin producer reads 0 bytes and gets full success at every layer: the relay accepts the event, --mention still notifies recipients, and the caller receives a real event_id. The recipient sees a blank message. Nothing anywhere reports a failure.

This isn't hypothetical — auditing one agent fleet's session logs found 10 confirmed producerless --content - sends over two weeks (the model composes prose in a variable, passes only the command string to its exec tool, and never pipes the content in). A second fleet independently hit the same class. Blank notifications read as agent malfunctions and cost real debugging time.

Fix

Add validate_message_content in validate.rs and call it in cmd_send_message immediately after read_or_stdin:

  • Rejects empty or whitespace-only content with exit 1 (user_error) and a message that points at the likely cause (--content - with a producer that sent nothing).
  • Attachment-only messages (--file with no caption) remain legitimate — the guard is gated on files.is_empty().
  • Deliberately not placed in read_or_stdin: that helper has ~11 call sites where empty passthrough is intentional (see the read_or_stdin_passthrough_empty_string test).

Verification

  • cargo test -p buzz-cli: 347 passed, 0 failed (4 new unit tests: nonempty ok, empty rejected, whitespace-only rejected, empty-with-files ok)
  • cargo clippy -p buzz-cli --all-targets: clean
  • cargo fmt -p buzz-cli --check: clean
  • Behavioral check on the built binary — fails before any network call:
$ printf '' | buzz messages send --channel <uuid> --content -
{"error":"user_error","message":"message content is empty; pass non-empty --content or attach --file (with `--content -`, an empty read usually means the stdin producer sent nothing)","retryable":false}
(exit 1)

🤖 Generated with Claude Code

An agent running `buzz messages send --content -` with a broken or missing
stdin producer reads 0 bytes, and every layer reports success: the relay
accepts the event, --mention still notifies, and the caller gets a real
event_id back — while the recipient sees a blank message. Observed
repeatedly in agent fleets (10 confirmed producerless sends over two weeks
in one deployment).

Guard at the send call site, not in read_or_stdin: that helper has other
call sites where empty passthrough is intentional. Attachment-only
messages (files with no caption) remain legitimate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Andrew Ivie <andrew1ivie@gmail.com>
@RockyMountainInfoTech
RockyMountainInfoTech requested a review from a team as a code owner August 13, 2026 22:36
@Chessing234

Copy link
Copy Markdown
Contributor

two other open PRs are on this exact ground: #5505 (reject blank message bodies at every write layer) and #5394 (reject empty stream messages at CLI, SDK and relay ingest). worth resolving to one before review — three branches editing the same send path will conflict.

on the diff itself: read_or_stdin has four other callers with the identical hazard — issues.rs:18 and :101, channels.rs:1067, and agents.rs:26/:64 for the system prompt. the failure you describe ("a broken stdin producer sends nothing, every layer reports success") is not specific to messages send; buzz issues create --body - will still publish an empty body.

@Chessing234 Chessing234 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same failure mode is still open one function down — cmd_send_diff_message reads read_or_stdin(&p.diff) at messages.rs:728 with no empty guard, so messages send-diff --diff - against a dead producer publishes an empty diff block and reports success exactly the way you describe. the fleet-log rationale covers that call just as well.

rest checks out: files is the only attachment field on SendMessageParams so the gate isn't over-broad, and CliError::Usage really is exit 1 / user_error (error.rs:92,114).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants