Skip to content

[Bug] Fix Feishu p2p DM replies fail with SUBSCRIPTION_NOT_FOUND (fixes #83730)#94760

Merged
obviyus merged 2 commits into
openclaw:mainfrom
xydigit-zt:fix/issue-83730-feishu-p2p-dm-target
Jun 30, 2026
Merged

[Bug] Fix Feishu p2p DM replies fail with SUBSCRIPTION_NOT_FOUND (fixes #83730)#94760
obviyus merged 2 commits into
openclaw:mainfrom
xydigit-zt:fix/issue-83730-feishu-p2p-dm-target

Conversation

@xydigit-zt

@xydigit-zt xydigit-zt commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Summary

What problem does this PR solve?

Non-thread Feishu p2p (DM) replies are routed to chat:<oc_xxx> (the raw p2p chat ID) instead of user:<open_id>, causing SUBSCRIPTION_NOT_FOUND errors when the bot attempts to reply.

Why does this matter now?

Issue #83730 is a P1 regression introduced in OpenClaw 2026.5.12. After skipReplyToInMessages suppresses reply metadata for non-thread DMs, the visible reply becomes a top-level send to the oc_* chat target — which Feishu rejects.

What is the intended outcome?

  • Non-thread p2p DM replies are sent to user:<open_id> instead of chat:<oc_xxx>
  • Thread p2p DM replies and group replies are unaffected
  • Streaming start, typing indicators, and conversation type detection keep using chatId as before

What is intentionally out of scope?

  • No changes to other channels or the Feishu send/target resolution layer
  • No changes to send-target.ts, targets.ts, or send.ts

What does success look like?

sendMessageFeishu, sendMediaFeishu, and sendStructuredCardFeishu receive user:ou_* as the to: target for non-thread p2p replies instead of oc_*.

Linked context

Closes #83730

Related clawtributors: #83764 (closed unmerged), #90636 (closed unmerged), #94761 (sibling PR, different approach)

Real behavior proof (required for external PRs)

  • Behavior or issue addressed: Non-thread Feishu p2p DM replies use user:<open_id> target instead of raw chatId
  • Real environment tested: Local checkout of openclaw fix branch (commit 8b01486) + live Feishu bot connected via WebSocket (appId cli_a9f668faaab89bd1)
  • Exact steps or command run after this patch:
    # Build OpenClaw from fix branch
    # Start gateway with Feishu channel
    OPENCLAW_CONFIG_PATH=~/feishu-test/openclaw.json \
      OPENCLAW_STATE_DIR=~/feishu-test/data \
      pnpm openclaw gateway run --allow-unconfigured --auth none
    # Send DM to bot via Feishu app
    
  • Evidence after fix:
    # Gateway logs from live Feishu p2p DM test (user IDs redacted)
    
    2026-06-19T16:30:42.334 [feishu] received message from ou_REDACTED in oc_REDACTED (p2p)
    2026-06-19T16:30:42.343 [feishu] Feishu[default] DM from ou_REDACTED: 你好你好你好
    2026-06-19T16:30:42.348 [feishu] dispatching to agent (session=agent:main:main)
    2026-06-19T16:30:59.555 [feishu] dispatch complete (queuedFinal=true, replies=1)
    
    2026-06-19T16:31:11.217 [feishu] received message from ou_REDACTED in oc_REDACTED (p2p)
    2026-06-19T16:31:11.222 [feishu] Feishu[default] DM from ou_REDACTED: 你是哪里人?
    2026-06-19T16:31:11.225 [feishu] dispatching to agent (session=agent:main:main)
    2026-06-19T16:31:21.116 [feishu] dispatch complete (queuedFinal=true, replies=1)
    
    Feishu chat screenshot
  • Observed result after fix: The AI replied to both DMs successfully. The chat screenshot shows the bot replying. No SUBSCRIPTION_NOT_FOUND errors in any log line.
  • What was not tested: Group chat replies and thread DM replies (same code path, expected unaffected). Streaming cards fall back to non-streaming delivery due to a pre-existing card creation HTTP 400 (unrelated to this dispatch target fix).
  • Proof limitations: The bot Feishu app lacks contact:contact.base:readonly scope (stale, non-blocking). Streaming card creation returns HTTP 400 for a pre-existing permission reason, so streaming cards fall back to non-streaming card delivery. The fix does not address this unrelated card creation issue.
  • Before evidence (optional): On current main, createFeishuReplyDispatcher constructs all visible sends with to: chatId (the oc_* p2p chat ID), which Feishu rejects with SUBSCRIPTION_NOT_FOUND for non-thread DM replies.

Tests and validation

  • CI=true node node_modules/.bin/vitest run extensions/feishu/src/reply-dispatcher.test.ts — 82 passed
  • CI=true node node_modules/.bin/vitest run extensions/feishu/src/bot.test.ts — 87 passed
  • CI=true node node_modules/.bin/vitest run extensions/feishu/src/channel.test.ts extension/feishu/src/send.test.ts extension/feishu/src/send-target.test.ts — 84 passed
  • All 253 tests green

Regression coverage: All existing Feishu reply-dispatcher and bot tests continue to pass. The fix is applied at the dispatcher parameter level. Streaming card delivery now also routes through effectiveSendTarget with suppressed reply metadata and normalized receive ID prefix.

Risk checklist

Did user-visible behavior change? (Yes)

Yes — non-thread Feishu p2p DM replies now go to user:<open_id> instead of chat:<oc_xxx>, fixing the SUBSCRIPTION_NOT_FOUND error.

Did config, environment, or migration behavior change? (No)

Did security, auth, secrets, network, or tool execution behavior change? (No)

What is the highest-risk area? The to: target change affects all visible sends (text, card, media, no-visible-reply fallback). Streaming start and conversation type detection are unchanged.

How is that risk mitigated? The sendTarget parameter defaults to undefined, falling back to chatId. Both dispatcher call sites in bot.ts pass the pre-computed feishuTo value. Live Feishu p2p DM test confirms two successful dispatches.

Current review state

What is the next action?

Maintainer / ClawSweeper review

What is still waiting on author, maintainer, CI, or external proof?

ClawSweeper re-review verdict. CI checks. Live Feishu p2p DM evidence (logs + screenshot) included above.

@openclaw-barnacle openclaw-barnacle Bot added channel: feishu Channel integration: feishu size: S triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels Jun 19, 2026
@clawsweeper

clawsweeper Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 21, 2026, 8:28 PM ET / 00:28 UTC.

Summary
The branch adds an optional Feishu reply-dispatcher sendTarget, passes the precomputed feishuTo target from both bot dispatch paths, and uses it for text, media, fallback, structured-card, and streaming-card delivery.

PR surface: Source +10. Total +10 across 2 files.

Reproducibility: yes. Current main is source-reproducible: it computes user:<open_id> for Feishu DMs but still passes raw ctx.chatId into the dispatcher while non-thread DM reply metadata is suppressed; I did not run a credentialed current-main Feishu tenant flow.

Review metrics: 1 noteworthy metric.

  • Delivery paths retargeted: 5 visible send paths plus 1 streaming-card start. The same target split now governs Feishu text, media, fallback, structured-card, and streaming delivery, so message-delivery review matters before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #83730
Summary: This PR is the active candidate fix for the canonical Feishu p2p SUBSCRIPTION_NOT_FOUND delivery issue; older same-root PRs are closed unmerged and a nearby replies=0 report remains a partial overlap with a different symptom.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Add narrow regression tests for p2p sendTarget, unchanged group/thread behavior, and streaming-card prefix normalization if maintainers want lower drift risk before merge.

Risk before merge

  • [P2] The diff changes visible Feishu delivery target selection across text, media, no-visible fallback, structured-card, and streaming-card paths, so a missed split could still misdeliver or suppress a reply.
  • [P1] Live proof covers the reported non-thread p2p DM path; group replies and p2p thread behavior remain source-reviewed rather than live-tested.
  • [P1] No focused regression tests were added for the new sendTarget split, so future drift is still guarded mainly by review rather than tests.

Maintainer options:

  1. Land with live p2p proof (recommended)
    Maintainers can proceed after required checks if the live p2p logs, inspected screenshot, and source-reviewed sibling paths are enough for this regression fix.
  2. Add target-split regression tests
    Before merge, add narrow assertions that non-thread p2p uses user:ou_* while group, p2p thread, media, fallback, and streaming-card paths keep the expected target semantics.
  3. Pause for Feishu owner proof
    If maintainers require live group/thread or streaming proof rather than source review, pause the PR for a Feishu-capable owner to capture that coverage.

Next step before merge

  • No automated repair blocker remains; a maintainer needs to accept the message-delivery risk or request focused regression tests before merge.

Security
Cleared: The diff only changes Feishu channel routing arguments and does not touch credentials, permissions, dependency sources, workflows, package metadata, or code-execution paths.

Review details

Best possible solution:

Land the separate Feishu delivery-target split after maintainer review and required checks, adding focused p2p/group/thread/streaming assertions if maintainers want lower future drift risk.

Do we have a high-confidence way to reproduce the issue?

Yes. Current main is source-reproducible: it computes user:<open_id> for Feishu DMs but still passes raw ctx.chatId into the dispatcher while non-thread DM reply metadata is suppressed; I did not run a credentialed current-main Feishu tenant flow.

Is this the best way to solve the issue?

Yes. The separate sendTarget boundary is the best fix found because raw chatId remains available for lifecycle, typing, thread, and context state while visible delivery uses the Feishu user/open-id target.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against dbb58341b51b.

Label changes

Label justifications:

  • P1: The PR targets a linked Feishu p2p DM regression where affected users receive no assistant reply after upgrading.
  • merge-risk: 🚨 message-delivery: The diff changes Feishu visible-reply target selection across multiple delivery paths.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (logs): The PR body supplies live Feishu WebSocket logs plus an inspected chat screenshot showing after-fix p2p DM replies without SUBSCRIPTION_NOT_FOUND.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body supplies live Feishu WebSocket logs plus an inspected chat screenshot showing after-fix p2p DM replies without SUBSCRIPTION_NOT_FOUND.
Evidence reviewed

PR surface:

Source +10. Total +10 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 2 18 8 +10
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 18 8 +10

What I checked:

Likely related people:

  • Vincentwei1021: Merged PR metadata for feat(feishu): skip reply-to in DM conversations #13211 shows this handle authored the Feishu DM skipReplyToInMessages behavior that made visible delivery depend on the dispatcher target. (role: introduced related DM reply behavior; confidence: high; commits: 56839d95bccc, d9230b13a4ad; files: extensions/feishu/src/bot.ts, extensions/feishu/src/reply-dispatcher.ts)
  • nszhsl: Merged PR metadata for feat(feishu): structured cards with identity header, note footer, and streaming enhancements #29938 ties this handle to the structured-card and streaming-card paths this PR retargets. (role: streaming-card feature author; confidence: high; commits: 39758c577257, df3a247db2a9; files: extensions/feishu/src/reply-dispatcher.ts, extensions/feishu/src/send.ts, extensions/feishu/src/streaming-card.ts)
  • Takhoffman: PR metadata shows this handle merged the DM reply behavior and structured-card PRs, and authored Feishu session-binding work adjacent to direct-message routing. (role: merger and adjacent Feishu contributor; confidence: medium; commits: d9230b13a4ad, df3a247db2a9, 89e3969d640c; files: extensions/feishu/src/bot.ts, extensions/feishu/src/reply-dispatcher.ts, extensions/feishu/src/streaming-card.ts)
  • vincentkoc: Current-main shallow blame and recent Feishu file history tie this handle to the latest broad Feishu bot and reply-dispatcher materialization in this checkout. (role: recent area contributor; confidence: medium; commits: 179eb155547a, d609f71c9b74; files: extensions/feishu/src/bot.ts, extensions/feishu/src/reply-dispatcher.ts)
  • m1heng: The Feishu package metadata identifies the bundled plugin as community maintained by this handle, making it useful routing context for Feishu channel behavior. (role: documented plugin contact; confidence: medium; files: extensions/feishu/package.json)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jun 19, 2026
@xydigit-zt
xydigit-zt force-pushed the fix/issue-83730-feishu-p2p-dm-target branch from 3e5470c to 2adaa1f Compare June 19, 2026 02:47
@xydigit-zt

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@xydigit-zt

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@openclaw-barnacle openclaw-barnacle Bot added triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. and removed triage: mock-only-proof Candidate: PR proof only shows tests, mocks, snapshots, lint, typecheck, or CI. labels Jun 19, 2026
@xydigit-zt

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui size: M and removed size: XS labels Jun 19, 2026
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. label Jun 19, 2026
@xydigit-zt
xydigit-zt force-pushed the fix/issue-83730-feishu-p2p-dm-target branch from 83cdcb8 to 8b01486 Compare June 19, 2026 07:53
@xydigit-zt

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@openclaw-barnacle openclaw-barnacle Bot added size: XS and removed app: web-ui App: web-ui size: M labels Jun 19, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels Jun 19, 2026
@xydigit-zt

Copy link
Copy Markdown
Contributor Author

The branch has been cleaned up to only contain the Feishu fix (2 files, +18/-8). All ClawSweeper findings have been addressed:

  • Streaming card delivery target fixed (effectiveSendTarget)
  • Streaming receive ID prefix normalized (user: → ou_xxx)
  • Streaming card reply metadata suppressed (sendReplyToMessageId)
  • Real Feishu live test evidence in PR body (gateway logs + AI reply screenshot)

@vincentkoc could you please take a look?

@xydigit-zt

Copy link
Copy Markdown
Contributor Author

Live Feishu streaming test completed with additional permission (im:message.reactions:write_only):

Key logs from the test:

  • dispatch complete (queuedFinal=true, replies=1) — reply delivered successfully
  • No SUBSCRIPTION_NOT_FOUND errors in either test
  • Streaming cards fall back to non-streaming delivery due to pre-existing HTTP 400 on card creation (unrelated to this PR's fix)
  • AI replied with real content to "你是哪里人?"

Full gateway log available on request.

@clawsweeper re-review

@xydigit-zt

Copy link
Copy Markdown
Contributor Author
IMG_04F54B4F1D29-1

@xydigit-zt

Copy link
Copy Markdown
Contributor Author

PR body updated with streaming test evidence and screenshot. @clawsweeper re-review

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 19, 2026
@openclaw-barnacle openclaw-barnacle Bot added triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. proof: sufficient ClawSweeper judged the real behavior proof convincing. labels Jun 19, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label Jun 19, 2026
@clawsweeper clawsweeper Bot removed the proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. label Jun 21, 2026
@xydigit-zt

Copy link
Copy Markdown
Contributor Author

Friendly ping 🙏 This is a P1 user-facing bug — Feishu p2p DM replies are currently failing with SUBSCRIPTION_NOT_FOUND, which means affected users see silent message drops. The fix is minimal (2 files, +18/−8) and has been cleared by ClawSweeper (platinum). Would appreciate a look when you have a moment, even a quick sanity check on the routing logic would help unblock this. Thanks!

xydigit-zt and others added 2 commits June 30, 2026 10:39
Add sendTarget param to createFeishuReplyDispatcher that overrides
the to: field for visible sends. Fix streaming card delivery target
by stripping routing prefixes (user:, chat:) before the Feishu
streaming API call. Route streaming card reply metadata through
suppressed sendReplyToMessageId.

Closes openclaw#83730
@obviyus
obviyus force-pushed the fix/issue-83730-feishu-p2p-dm-target branch from 850f36e to 7126c25 Compare June 30, 2026 17:39
@obviyus
obviyus merged commit f078962 into openclaw:main Jun 30, 2026
38 checks passed
@obviyus

obviyus commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Landed via rebase onto main.

  • Scoped tests: node scripts/run-vitest.mjs extensions/feishu/src/reply-dispatcher.test.ts; node scripts/run-vitest.mjs extensions/feishu/src/bot.test.ts
  • Review: .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main --prompt 'Quick closeout review for PR #94760...' clean
  • Changelog: release-owned; not edited for this PR
  • Land commit: 7126c25
  • Merge commit: f078962

Thanks @xydigit-zt!

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

Labels

channel: feishu Channel integration: feishu merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Feishu p2p DM replies fail with SUBSCRIPTION_NOT_FOUND after upgrading to 2026.5.12

2 participants