#19387: Fix #19302: Filter isError payloads before heartbeat selection
channel: whatsapp-web
size: S
Cluster:
Error Payload Filtering
## Problem
Tool error summaries (e.g., `⚠️ 🛠️ Exec: ...`) are delivered to channels even when agent responds with `HEARTBEAT_OK`.
## Root Cause
`resolveHeartbeatReplyPayload` iterates backwards and picks the error payload over `HEARTBEAT_OK` responses.
## Solution
Filter out error payloads (where `isError === true`) before passing to `resolveHeartbeatReplyPayload`.
## Files Modified
- `src/infra/heartbeat-runner.ts` (line 661)
- `src/web/auto-reply/heartbeat-runner.ts` (line 176)
- `src/auto-reply/heartbeat-reply-payload.test.ts` (new test file)
## Test Coverage
Comprehensive test suite including:
- Basic payload resolution behavior
- Error payload filtering
- Mixed payload arrays
- Edge cases
All tests passing ✅
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes issue #19302, where tool error summary payloads (with `isError: true`) were being selected as the heartbeat reply instead of the `HEARTBEAT_OK` response. The fix filters `isError` payloads from the array before it is passed to `resolveHeartbeatReplyPayload` in both `src/infra/heartbeat-runner.ts` and `src/web/auto-reply/heartbeat-runner.ts`.
**Key changes:**
- Both heartbeat runners now pre-filter `isError` payloads from array results before selecting the reply payload.
- A new test file `src/auto-reply/heartbeat-reply-payload.test.ts` adds coverage for `resolveHeartbeatReplyPayload`.
**Issues found:**
- The `BUG #19302` tests in the new test file perform filtering *within the test body* (calling `.filter((p) => !p.isError)` manually) rather than going through the production runners. This means the tests don't actually verify the fix in either heartbeat runner — a caller that forgets to pre-filter would not be caught by these tests. The tests would pass even if the filtering was removed from the production code.
- The fix is intentionally scoped to arrays: a single non-array `isError` payload is not filtered. This gap is documented in the test (line 73–81) but not addressed or discussed in the PR description.
- The inline filtering expression is duplicated across two files. If a third caller is ever added, the same pattern must be manually applied there too.
<h3>Confidence Score: 3/5</h3>
- The core fix is correct and addresses the reported bug, but the test suite validates the filtering in isolation rather than through the production code paths, reducing confidence in regression coverage.
- The production fix (filtering `isError` payloads in both heartbeat runners) is correct and solves the reported problem. However, the new tests test the filtering logic inline in the test body rather than exercising the actual runner code, meaning they wouldn't catch a regression if the filter was removed from the runners. Additionally, the fix is duplicated across two files rather than being centralized, creating a maintenance risk for future callers.
- src/auto-reply/heartbeat-reply-payload.test.ts — the BUG #19302 test suite does not cover the actual fix in the heartbeat runners
<sub>Last reviewed commit: a276b95</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#19406: fix(heartbeat): filter error payloads from heartbeat reply selection
by namabile · 2026-02-17
87.5%
#19339: fix(heartbeat): skip isError payloads when resolving heartbeat reply
by aldoeliacim · 2026-02-17
85.6%
#15575: fix(heartbeat): suppress prefixed HEARTBEAT_OK ack replies (#15505)
by TsekaLuk · 2026-02-13
79.2%
#12786: fix: drop heartbeat runs that arrive while another run is active
by mcaxtr · 2026-02-09
78.5%
#11647: fix(webchat): filter HEARTBEAT_OK messages from chat.history response
by liuxiaopai-ai · 2026-02-08
78.2%
#19745: fix(heartbeat): enforce interval check regardless of trigger source
by misterdas · 2026-02-18
78.1%
#11859: fix: filter HEARTBEAT_OK messages from chat.history when showOk is ...
by Zjianru · 2026-02-08
78.0%
#17950: fix: filter error payloads from user-facing messages
by Suksham-sharma · 2026-02-16
77.9%
#17371: fix(heartbeat): always strip HEARTBEAT_OK token from reply text
by BinHPdev · 2026-02-15
77.0%
#21014: fix(cron): suppress main-session summary for HEARTBEAT_OK responses
by nickjlamb · 2026-02-19
76.6%