Skip to content

fix(slack): validate Slack API responses and fix silent delivery drops#375

Merged
Aaronontheweb merged 3 commits into
devfrom
claude-wt-slack-errors-3
Mar 21, 2026
Merged

fix(slack): validate Slack API responses and fix silent delivery drops#375
Aaronontheweb merged 3 commits into
devfrom
claude-wt-slack-errors-3

Conversation

@Aaronontheweb

Copy link
Copy Markdown
Collaborator

Summary

Fixes #371 — Slack delivery errors silently swallowed in production.

  • Root cause: SlackReplyClient.PostThreadReplyAsync() discarded the return value of Chat.PostMessage(), relying entirely on SlackNet to throw on ok:false. SlackNet 0.17.9's internal null-coalescing creates phantom Ok=true responses when the HTTP body is empty, causing delivery failures to be silently counted as successes.
  • Core fix: Capture and validate PostMessage/Upload responses — null or empty Ts throws SlackMessageDeliveryException with phantom_success error code
  • Outbound hardening: Added SlackException catch + response validation to SlackOutboundClient (previously had zero error handling)
  • Error classification: Added rate_limitedTransportFailure mapping; moved SlackBlockConverter.Convert() inside try blocks so block conversion errors are properly wrapped
  • Metrics fix: Replaced dead PlainTextFallback counter with Rejected counter for ok:false responses; separated Filtered (bot self-messages, duplicates) from Dropped (real message drops)
  • CLI: netclaw stats now shows posted/rejected/failed instead of posted/failed/plain_text_fallback

Breaking changes

  • DaemonStats.SlackActivity and DaemonRuntimeStatus.SlackCounters wire types: removed RepliesPlainTextFallback, added RepliesRejected. CLI and daemon ship together so version skew is not a concern.

Test plan

  • 15 new tests (13 unit + 2 integration), all passing
  • 1,221 total tests pass across all 7 test projects
  • dotnet slopwatch analyze — zero violations
  • Manual: trigger msg_too_long in production → verify rejected counter increments, DeliveryFailed reaches session, LLM retries with shorter output

#371)

SlackReplyClient discarded the return value of Chat.PostMessage(), relying
entirely on SlackNet to throw on ok:false. SlackNet's internal null-coalescing
creates phantom Ok=true responses when the HTTP body is empty, causing
delivery failures to be silently counted as successes.

- Capture and validate PostMessage/Upload responses (null or empty Ts = throw)
- Add SlackException catch + response validation to SlackOutboundClient
- Add rate_limited error code mapping to TransportFailure
- Replace dead PlainTextFallback counter with Rejected counter for ok:false
- Separate Filtered counter from Dropped for bot-message/duplicate filtering
- Update CLI stats display: posted/rejected/failed instead of posted/failed
… cases

Review feedback: SlackBlockConverter.Convert() was outside the try block,
so block conversion errors would escape as raw exceptions instead of being
wrapped as SlackMessageDeliveryException. Also added missing_scope and
no_permission to the MapFailureKind theory test coverage.
@Aaronontheweb Aaronontheweb enabled auto-merge (squash) March 21, 2026 21:41
@Aaronontheweb Aaronontheweb merged commit 04fa85e into dev Mar 21, 2026
3 checks passed
@Aaronontheweb Aaronontheweb deleted the claude-wt-slack-errors-3 branch March 21, 2026 21:48
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.

bug: Slack delivery errors still silently dropped — LLM response never reaches user

1 participant