Skip to content

fix(followup): fall back to dispatcher when same-channel origin routing fails#26109

Merged
steipete merged 2 commits intoopenclaw:mainfrom
Sid-Qin:fix/feishu-dm-reply-routing-25767
Feb 25, 2026
Merged

fix(followup): fall back to dispatcher when same-channel origin routing fails#26109
steipete merged 2 commits intoopenclaw:mainfrom
Sid-Qin:fix/feishu-dm-reply-routing-25767

Conversation

@Sid-Qin
Copy link
Contributor

@Sid-Qin Sid-Qin commented Feb 25, 2026

Summary

  • Problem: Feishu DM replies are silently dropped when routeReply fails (e.g., outbound adapter unavailable, plugin not fully loaded, API error), making the Feishu channel unusable
  • Why it matters: 100% repro — users never receive agent replies in Feishu DM
  • What changed: In sendFollowupPayloads (src/auto-reply/reply/followup-runner.ts), when routeReply fails and originatingChannel === messageProvider (same channel), the reply now falls back to onBlockReply — the dispatcher created by the same channel's handler
  • What did NOT change: Cross-channel routing failures (origin ≠ provider) still drop the payload to preserve origin isolation

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

Feishu DM replies now reach users when the outbound routing adapter fails. Previously the reply was silently dropped; now it falls back to the channel's native dispatcher.

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • Integration/channel: Feishu (DM)

Steps

  1. Configure Feishu channel with DM enabled
  2. Send a DM message to the bot from Feishu
  3. Bot processes the message and generates a reply

Expected

  • Reply is sent back to Feishu DM

Actual (before fix)

  • Reply is silently dropped; followup queue: route-reply failed logged but no fallback attempted

Evidence

  • Failing test/log before + passing after
  • New test: same-channel fallback (feishu → feishu) delivers via onBlockReply when routeReply fails
  • Existing test: cross-channel (discord origin, webchat provider) still does NOT fall back
  • All 471 auto-reply/reply tests pass

Human Verification (required)

  • Verified scenarios: same-channel fallback (feishu→feishu), cross-channel isolation (discord origin + webchat provider)
  • Edge cases checked: missing messageProvider, missing onBlockReply, successful routeReply (no fallback triggered)
  • What you did not verify: live Feishu bot end-to-end

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Revert commit 13b4b6b
  • Files/config to restore: src/auto-reply/reply/followup-runner.ts
  • Known bad symptoms: Same-channel replies being double-delivered (once via routeReply success + once via fallback)

Risks and Mitigations

  • Risk: If routeReply returns { ok: false } but the message was actually delivered (partial success), the fallback could cause a duplicate
    • Mitigation: routeReply only returns ok: false when delivery definitively failed; the fallback condition also requires originatingChannel === messageProvider to prevent cross-channel leakage

SidQin-cyber and others added 2 commits February 25, 2026 04:49
…ng fails

When routeReply fails for an originating channel that matches the
session's messageProvider, the onBlockReply callback was created by
that same channel's handler and can safely deliver the reply.
Previously the payload was silently dropped on any routeReply failure,
causing Feishu DM replies to never reach the user.

Cross-channel fallback (origin ≠ provider) still drops the payload to
preserve origin isolation.

Closes openclaw#25767

Co-authored-by: Cursor <cursoragent@cursor.com>
@steipete steipete force-pushed the fix/feishu-dm-reply-routing-25767 branch from 13b4b6b to 0ca4412 Compare February 25, 2026 04:52
@steipete steipete merged commit f7de41c into openclaw:main Feb 25, 2026
10 checks passed
@steipete
Copy link
Contributor

Landed via temp rebase onto main.

  • Gate: pnpm test src/auto-reply/reply/followup-runner.test.ts && pnpm check
  • Land commit: 0ca4412
  • Merge commit: f7de41c

Thanks @Sid-Qin!

steipete added a commit to justinhuangcode/openclaw that referenced this pull request Feb 25, 2026
…ng fails (openclaw#26109)

* fix(followup): fall back to dispatcher when same-channel origin routing fails

When routeReply fails for an originating channel that matches the
session's messageProvider, the onBlockReply callback was created by
that same channel's handler and can safely deliver the reply.
Previously the payload was silently dropped on any routeReply failure,
causing Feishu DM replies to never reach the user.

Cross-channel fallback (origin ≠ provider) still drops the payload to
preserve origin isolation.

Closes openclaw#25767

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: allow same-channel followup fallback routing (openclaw#26109) (thanks @Sid-Qin)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Jackson3195 pushed a commit to Jackson3195/openclaw-with-a-personal-touch that referenced this pull request Feb 25, 2026
…ng fails (openclaw#26109)

* fix(followup): fall back to dispatcher when same-channel origin routing fails

When routeReply fails for an originating channel that matches the
session's messageProvider, the onBlockReply callback was created by
that same channel's handler and can safely deliver the reply.
Previously the payload was silently dropped on any routeReply failure,
causing Feishu DM replies to never reach the user.

Cross-channel fallback (origin ≠ provider) still drops the payload to
preserve origin isolation.

Closes openclaw#25767

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: allow same-channel followup fallback routing (openclaw#26109) (thanks @Sid-Qin)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
brianleach pushed a commit to brianleach/openclaw that referenced this pull request Feb 26, 2026
…ng fails (openclaw#26109)

* fix(followup): fall back to dispatcher when same-channel origin routing fails

When routeReply fails for an originating channel that matches the
session's messageProvider, the onBlockReply callback was created by
that same channel's handler and can safely deliver the reply.
Previously the payload was silently dropped on any routeReply failure,
causing Feishu DM replies to never reach the user.

Cross-channel fallback (origin ≠ provider) still drops the payload to
preserve origin isolation.

Closes openclaw#25767

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: allow same-channel followup fallback routing (openclaw#26109) (thanks @Sid-Qin)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
execute008 pushed a commit to execute008/openclaw that referenced this pull request Feb 27, 2026
…ng fails (openclaw#26109)

* fix(followup): fall back to dispatcher when same-channel origin routing fails

When routeReply fails for an originating channel that matches the
session's messageProvider, the onBlockReply callback was created by
that same channel's handler and can safely deliver the reply.
Previously the payload was silently dropped on any routeReply failure,
causing Feishu DM replies to never reach the user.

Cross-channel fallback (origin ≠ provider) still drops the payload to
preserve origin isolation.

Closes openclaw#25767

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: allow same-channel followup fallback routing (openclaw#26109) (thanks @Sid-Qin)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
r4jiv007 pushed a commit to r4jiv007/openclaw that referenced this pull request Feb 28, 2026
…ng fails (openclaw#26109)

* fix(followup): fall back to dispatcher when same-channel origin routing fails

When routeReply fails for an originating channel that matches the
session's messageProvider, the onBlockReply callback was created by
that same channel's handler and can safely deliver the reply.
Previously the payload was silently dropped on any routeReply failure,
causing Feishu DM replies to never reach the user.

Cross-channel fallback (origin ≠ provider) still drops the payload to
preserve origin isolation.

Closes openclaw#25767

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: allow same-channel followup fallback routing (openclaw#26109) (thanks @Sid-Qin)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
zooqueen pushed a commit to hanzoai/bot that referenced this pull request Mar 6, 2026
…ng fails (openclaw#26109)

* fix(followup): fall back to dispatcher when same-channel origin routing fails

When routeReply fails for an originating channel that matches the
session's messageProvider, the onBlockReply callback was created by
that same channel's handler and can safely deliver the reply.
Previously the payload was silently dropped on any routeReply failure,
causing Feishu DM replies to never reach the user.

Cross-channel fallback (origin ≠ provider) still drops the payload to
preserve origin isolation.

Closes openclaw#25767

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: allow same-channel followup fallback routing (openclaw#26109) (thanks @Sid-Qin)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
thebenjaminlee pushed a commit to escape-velocity-ventures/openclaw that referenced this pull request Mar 7, 2026
…ng fails (openclaw#26109)

* fix(followup): fall back to dispatcher when same-channel origin routing fails

When routeReply fails for an originating channel that matches the
session's messageProvider, the onBlockReply callback was created by
that same channel's handler and can safely deliver the reply.
Previously the payload was silently dropped on any routeReply failure,
causing Feishu DM replies to never reach the user.

Cross-channel fallback (origin ≠ provider) still drops the payload to
preserve origin isolation.

Closes openclaw#25767

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: allow same-channel followup fallback routing (openclaw#26109) (thanks @Sid-Qin)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Feishu message reply routed to wrong channel

2 participants