#21174: fix(bluebubbles): trim leading newlines from message text
channel: bluebubbles
size: XS
Cluster:
Bluebubbles iMessage Fixes
## Summary
Describe the problem and fix in 2–5 bullets:
- Problem: iMessage bubbles sent via BlueBubbles often contain unsightly leading newlines, especially when the
response follows a tool call or is constructed from multiple blocks.
- Why it matters: This creates empty vertical space at the top of messages, degrading the reading experience o
iOS/macOS.
- What changed: Applied `.trimStart()` in three key locations:
1. The `BlockReplyCoalescer` (to strip artifacts during message assembly).
2. The BlueBubbles `send.ts` adapter (defensive trimming).
3. The BlueBubbles `channel.ts` outbound handler (final safeguard).
- What did NOT change (scope boundary): Strictly limits changes to whitespace handling in the BlueBubbles extension and the reply coalescer; no logic changes to message routing.
## Change Type (select all)
- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Docs
- [ ] Security hardening
- [ ] Chore/infra
## Scope (select all touched areas)
- [x] Gateway / orchestration
- [ ] Skills / tool execution
- [ ] Auth / tokens
- [ ] Memory / storage
- [x] Integrations
- [ ] API / contracts
- [ ] UI / DX
- [ ] CI/CD / infra
## Linked Issue/PR
- Closes #12770
- Related #
## User-visible / Behavior Changes
List user-visible changes (including defaults/config).
If none, write `None`.
- Messages sent via the BlueBubbles channel will no longer have leading blank lines/whitespace, especially when following tool outputs.
- Reply blocks will be coalesced more cleanly without redundant newlines.
## Security Impact (required)
- New permissions/capabilities? (`Yes/No`) No
- Secrets/tokens handling changed? (`Yes/No`) No
- New/changed network calls? (`Yes/No`) No
- Command/tool execution surface changed? (`Yes/No`)No
- Data access scope changed? (`Yes/No`) No
- If any `Yes`, explain risk + mitigation: None
## Repro + Verification
### Environment
- OS: macOS
- Runtime/container: Node.js 22+
- Model/provider: Any LLM, model-agnostic whitespace handling.
- Integration/channel (if any): BlueBubbles
- Relevant config (redacted): None
### Steps
1. Configure BlueBubbles channel.
2. Trigger a response that includes a tool call followed by text (which often outputs a leading newline).
3. Observe the message bubble in iMessage.
### Expected
-- The message text starts immediately at the top of the bubble.
### Actual
-The message text starts after one or more blank lines.
## Evidence
Attach at least one:
- [ ] Failing test/log before + passing after
- [x] Trace/log snippets
- [ ] Screenshot/recording
- [ ] Perf numbers (if relevant)
###
"Verified with a reproduction script that mocks the BlockReplyCoalescer. The log shows that the second block (which
started with \n\n) was correctly trimmed before being joined."
###
```text
Simulating: Block 1 (e.g. tool output) followed by Block 2 (text with leading newlines)...
--- FLUSHED PAYLOAD ---
Payload Text: "Some tool output...\n\nHere is the result of your request."
PASS: Payload joined correctly with standard joiner (\n\n).
-----------------------
```
## Human Verification (required)
What you personally verified (not just CI), and how:
- Verified scenarios: Checked that sendMessageBlueBubbles correctly trims input strings. Verified that BlockReplyCoalescer trims text segments before joining.
- Edge cases checked: Empty strings, strings with only whitespace (handled by existing logic + new trim), standard text messages.
- What you did **not** verify: Full end-to-end BlueBubbles server interaction (mocked/unit tested logic).
-
## Compatibility / Migration
- Backward compatible? (`Yes/No`) Yes
- Config/env changes? (`Yes/No`) No
- Migration needed? (`Yes/No`) No
- If yes, exact upgrade steps: None
## Failure Recovery (if this breaks)
- How to disable/revert this change quickly: Revert the commit.
- Files/config to restore: extensions/bluebubbles/src/channel.ts, extensions/bluebubbles/src/send.ts, src/auto-reply/reply/block-reply-coalescer.ts
- Known bad symptoms reviewers should watch for: Messages appearing "glued together" if the joiner logic fails (unlikely given testing).
## Risks and Mitigations
List only real risks for this PR. Add/remove entries as needed. If none, write `None`.
- Risk: None
- Mitigation: None
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Trims leading whitespace (spaces, tabs, newlines) from BlueBubbles message text before sending. The change prevents messages with leading newlines from appearing with unwanted blank lines at the start.
- Changed `text` to `text.trimStart()` in `channel.ts:317` when calling `sendMessageBlueBubbles`
- Changed `text ?? ""` to `(text ?? "").trimStart()` in `send.ts:325` inside `sendMessageBlueBubbles`
- Changed concatenated text to `text.trimStart()` in `block-reply-coalescer.ts:117` when coalescing message chunks
The fix is applied consistently across the BlueBubbles channel and shared reply coalescer, matching the pattern used in other channels like iMessage (`src/imessage/send.ts:75`, `src/imessage/send.ts:78`) and Signal (`src/signal/format.ts:329`).
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- The change is straightforward, well-scoped, and follows established patterns in the codebase. `trimStart()` is applied consistently across three related locations. The logic handles empty text correctly (checks `trimmedText.trim()` after trimming), and subsequent markdown stripping ensures messages like "***" don't become empty. The pattern matches existing usage in iMessage and Signal channels.
- No files require special attention
<sub>Last reviewed commit: 4a2fbe5</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#23483: fix(bluebubbles): key debounce by chat+sender instead of messageId
by saucesteals · 2026-02-22
80.3%
#10612: fix: trim leading blank lines on first emitted chunk only (#5530)
by 1kuna · 2026-02-06
77.3%
#15240: fix(bluebubbles): URL dropped when sent in same iMessage bubble as ...
by yinghaosang · 2026-02-13
76.5%
#16304: fix(bluebubbles): accept webhook message fields at top level
by MisterGuy420 · 2026-02-14
75.6%
#8052: fix(whatsapp): strip leading whitespace from outbound messages
by FelixFoster · 2026-02-03
75.3%
#16733: fix(ui): avoid injected newlines when tool output is hidden
by jp117 · 2026-02-15
74.1%
#12325: fix: trim leading/trailing whitespace from outbound messages
by jordanstern · 2026-02-09
74.1%
#22564: fix(bluebubbles): include iMessage subject in message text
by lailoo · 2026-02-21
74.1%
#19522: feat(bluebubbles): send TTS as native iMessage voice memos
by mwmacmahon · 2026-02-17
73.7%
#11600: fix(bluebubbles): always use private-api method for sending
by coletebou · 2026-02-08
72.3%