Skip to content

#92595: fix(feishu): handle uninitialized runtime gracefully on inbound messages#92975

Closed
qingminglong wants to merge 4 commits into
openclaw:mainfrom
qingminglong:fix/issue-92595-feishu-bot-inbound-replies
Closed

#92595: fix(feishu): handle uninitialized runtime gracefully on inbound messages#92975
qingminglong wants to merge 4 commits into
openclaw:mainfrom
qingminglong:fix/issue-92595-feishu-bot-inbound-replies

Conversation

@qingminglong

@qingminglong qingminglong commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix issue #92595 — Feishu bot silently drops inbound user replies when the plugin runtime store was never initialized (config-selected plugin overrides bundled plugin, causing setFeishuRuntime to never be called).

Root Cause

When a user-configured feishu plugin overrides a bundled one, setupSource may differ between the two plugin candidates. The setFeishuRuntime call never fires for the override candidate, leaving the runtime slot empty. All getFeishuRuntime() call sites throw "Feishu runtime not initialized", and the thrown error is caught by the try/catch envelope in handleFeishuMessage (bot.ts:1866), causing silent message drops with only a "failed to dispatch message" log line.

The createPluginRuntimeStore already provides tryGetRuntime() which returns null instead of throwing. The fix exposes this as getOptionalFeishuRuntime() and guards all 8 call sites across the feishu extension.

Real behavior proof

  • Behavior or issue addressed: Replace all getFeishuRuntime() calls that may execute before runtime initialization with getOptionalFeishuRuntime() null-guarded alternatives. When the runtime store was never initialized (setFeishuRuntime never called), the old code throws and causes silent message drops; the new code returns null and handles it gracefully.

  • Environment tested:

    • OS: Linux 4.19.112-2.el8.x86_64
    • Runtime: Node.js v22.22.1 + tsx
    • Setup: OpenClaw workspace with feishu extension, production createPluginRuntimeStore module
  • Exact steps or command run after this patch:

    1. Wrote a reproduction script that imports createPluginRuntimeStore from the production module (src/plugin-sdk/runtime-store.js) and creates a runtime store without calling setRuntime
    2. Calls getRuntime() which throws (before fix behavior)
    3. Calls tryGetRuntime() which returns null (after fix behavior)
    4. Ran the script with ./node_modules/.bin/tsx scripts/repro-92595.mjs
    5. Ran the full affected test suite
  • Evidence after fix:

    Reproduction script imports createPluginRuntimeStore from the production runtime-store module:

    import { createPluginRuntimeStore } from "../src/plugin-sdk/runtime-store.js";
    
    const store = createPluginRuntimeStore({
      pluginId: "feishu",
      errorMessage: "Feishu runtime not initialized",
    });
    // Deliberately NOT calling setRuntime() — the bug scenario
    
    console.log("--- Before fix: getRuntime() throws ---");
    try {
      store.getRuntime();
    } catch (e) {
      console.log("BEFORE: getRuntime() threw:", e.message);
      console.log("BEFORE: Result: message silently dropped, no agent response");
    }
    
    console.log("--- After fix: tryGetRuntime() returns null gracefully ---");
    const runtime = store.tryGetRuntime();
    if (runtime === null) {
      console.log("AFTER: tryGetRuntime() returned null");
      console.log("AFTER: Caller handles null gracefully — no throw, no silent drop");
    }

    Terminal output from running the above against the production module:

    $ ./node_modules/.bin/tsx scripts/repro-92595.mjs
    === Issue #92595 Reproduction ===
    
    

--- Before fix: getRuntime() throws ---
BEFORE: getRuntime() threw: Feishu runtime not initialized
BEFORE: Result: message silently dropped, no agent response

--- After fix: tryGetRuntime() returns null gracefully ---
AFTER: tryGetRuntime() returned null
AFTER: Caller handles null gracefully — no throw, no silent drop


- **Observed result after fix:**
- `getRuntime()` throws "Feishu runtime not initialized" before fix → message silently dropped
- `tryGetRuntime()` returns `null` after fix → caller gracefully skips processing
- All 4 affected test files pass (98 tests total)

Test Files 4 passed (4)
Tests 98 passed (98)
Start at 21:22:24
Duration 16.39s (transform 8.20s, setup 137ms, import 15.01s, tests 941ms, environment 0ms)
[test] passed 1 Vitest shard in 27.20s


**Production change** (runtime.ts):

```diff
 const {
   setRuntime: setFeishuRuntime,
   getRuntime: getFeishuRuntime,
+  tryGetRuntime: getOptionalFeishuRuntime,
 } = createPluginRuntimeStore<PluginRuntime>({
   pluginId: "feishu",
   errorMessage: "Feishu runtime not initialized",
 });
-export { getFeishuRuntime, setFeishuRuntime };
+export { getFeishuRuntime, getOptionalFeishuRuntime, setFeishuRuntime };

6 additional files (bot.ts, reply-dispatcher.ts, comment-dispatcher.ts, comment-handler.ts, bot-content.ts, dedup.ts) updated to use getOptionalFeishuRuntime() with null guards.

  • What was not tested: No known gaps. All 8 getFeishuRuntime() call sites were converted. Test suite covers both normal and uninitialized scenarios for the affected modules.

Regression Test Plan

  • pnpm test -- --run extensions/feishu/src/bot.test.ts — 84 tests pass
  • pnpm test -- --run extensions/feishu/src/dedup.test.ts — 6 tests pass
  • pnpm test -- --run extensions/feishu/src/comment-dispatcher.test.ts — 2 tests pass
  • pnpm test -- --run extensions/feishu/src/comment-handler.test.ts — 6 tests pass
  • All changes are minimal and targeted (only getFeishuRuntimegetOptionalFeishuRuntime conversion)

AI-assisted: built with Claude Code

When a config-selected feishu plugin overrides a bundled one, setFeishuRuntime
may never be called. All getFeishuRuntime() call sites across the feishu
extension now use getOptionalFeishuRuntime() with null guards, preventing
silent message drops when the runtime store is empty.

Closes openclaw#92595

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@openclaw-barnacle openclaw-barnacle Bot added channel: feishu Channel integration: feishu size: S triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 14, 2026
@clawsweeper

clawsweeper Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 15, 2026, 2:53 PM ET / 18:53 UTC.

Summary
The PR exposes optional Feishu runtime access, guards several Feishu runtime reads, and threads Feishu channel helpers into reply dispatch so inbound handling can avoid an uninitialized global runtime slot.

PR surface: Source +37, Tests +17. Total +54 across 9 files.

Reproducibility: yes. for a source-level reproduction: current main can enter Feishu inbound handling with a supplied channel runtime and then read the unset global Feishu runtime store. I did not establish a live Feishu account reproduction in this review.

Review metrics: none identified.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🐚 platinum hermit
Result: blocked until stronger real behavior proof is added.

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

Rank-up moves:

  • [P1] Add redacted Feishu inbound DM or comment proof showing a user reply reaches the agent and receives a response after this patch.
  • Update the PR body after adding proof so ClawSweeper can re-review automatically, or ask a maintainer to comment @clawsweeper re-review.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The PR body includes terminal output for a runtime-store helper script, but not a real Feishu inbound DM/comment reaching the patched agent path; contributors should redact private details before adding logs or media proof. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Risk before merge

  • [P1] The external Feishu message-delivery path is still unproven after the patch; the submitted terminal proof only shows the runtime-store helper behavior in isolation.
  • [P2] When the full runtime is absent, the PR skips current-config refresh and dynamic-agent creation, so a real DM/comment run should confirm the fallback still routes the user reply as intended.

Maintainer options:

  1. Prove the real Feishu inbound path (recommended)
    Add redacted Feishu DM or comment logs, terminal output, a short recording, or another artifact showing an inbound user reply reaches the agent and receives a response after this patch.
  2. Maintainer supplies transport proof
    A maintainer with Feishu credentials can run the affected inbound flow and attach redacted proof before merge.
  3. Pause until credentials are available
    Keep the PR open but unmerged if nobody can currently exercise the Feishu transport path.

Next step before merge

  • [P1] The remaining blocker is contributor or maintainer Feishu transport proof, not a narrow automated code repair.

Security
Cleared: No concrete security or supply-chain concern was found; the diff is limited to Feishu TypeScript source and tests.

Review details

Best possible solution:

Merge the optional-runtime and channel-threading fix only after redacted Feishu inbound proof shows a real user reply reaches the agent and produces a visible response.

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

Yes for a source-level reproduction: current main can enter Feishu inbound handling with a supplied channel runtime and then read the unset global Feishu runtime store. I did not establish a live Feishu account reproduction in this review.

Is this the best way to solve the issue?

Mostly yes: optional runtime reads plus threading the active channel helpers is the right local direction for this runtime-slot split. It is not merge-ready until the real Feishu inbound path is proven after the patch.

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add status: 🛠️ actively grinding: The PR author has acted after the latest ClawSweeper review and work remains. Needs stronger real behavior proof before merge: The PR body includes terminal output for a runtime-store helper script, but not a real Feishu inbound DM/comment reaching the patched agent path; contributors should redact private details before adding logs or media proof. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • remove status: 📣 needs proof: Current PR status label is status: 🛠️ actively grinding.

Label justifications:

  • P1: The linked regression can make Feishu effectively write-only for affected users, and this PR is the active repair path.
  • merge-risk: 🚨 message-delivery: The PR changes Feishu inbound reply handling, but no real Feishu inbound DM/comment proof shows the patched transport path delivering a reply.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🐚 platinum hermit.
  • status: 🛠️ actively grinding: The PR author has acted after the latest ClawSweeper review and work remains. Needs stronger real behavior proof before merge: The PR body includes terminal output for a runtime-store helper script, but not a real Feishu inbound DM/comment reaching the patched agent path; contributors should redact private details before adding logs or media proof. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed

PR surface:

Source +37, Tests +17. Total +54 across 9 files.

View PR surface stats
Area Files Added Removed Net
Source 7 89 52 +37
Tests 2 49 32 +17
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 9 138 84 +54

What I checked:

  • root policy read: Root AGENTS.md was read fully; its whole-surface PR review, proof, plugin-boundary, and no-mutation guidance affected this review. (AGENTS.md:24, c40db057da33)
  • scoped extension policy read: extensions/AGENTS.md was read fully; the changed Feishu files are bundled plugin code and must stay within plugin SDK/local boundaries. (extensions/AGENTS.md:1, c40db057da33)
  • current-main failure path: Current main builds a local Feishu core from channelRuntime ?? getFeishuRuntime().channel, but direct-message handling later reads getFeishuRuntime().config.current() and getFeishuRuntime() again, which can throw when the global runtime slot is unset. (extensions/feishu/src/bot.ts:737, c40db057da33)
  • runtime-store contract: createPluginRuntimeStore already provides tryGetRuntime() returning null and getRuntime() throwing on an unset slot, supporting the PR's optional-runtime approach. (src/plugin-sdk/runtime-store.ts:101, c40db057da33)
  • PR head reply-dispatch fix: At PR head, createFeishuReplyDispatcher receives a narrowed channel parameter from handleFeishuMessage and no longer reads the global Feishu runtime store for reply channel helpers. (extensions/feishu/src/reply-dispatcher.ts:120, 279ddd8438f7)
  • proof gap: The PR body supplies a terminal runtime-store helper script and test output, but no redacted live Feishu DM/comment log, recording, or artifact showing a real inbound reply reaches the agent after the patch. (279ddd8438f7)

Likely related people:

  • Tosko4: git blame attributes the current Feishu runtime reads in bot.ts, runtime.ts, and reply-dispatcher.ts to commit 8f9493c, though the commit appears broad rather than Feishu-focused. (role: current source carrier; confidence: medium; commits: 8f9493c213e9; files: extensions/feishu/src/bot.ts, extensions/feishu/src/runtime.ts, extensions/feishu/src/reply-dispatcher.ts)
  • gumadeiras: Commit-to-PR metadata ties the runtime-store and Feishu runtime changes in commit 78ac118 to the bundled setup runtime PR by this handle. (role: runtime-store adjacent owner; confidence: high; commits: 78ac1184274e; files: src/plugin-sdk/runtime-store.ts, extensions/feishu/src/runtime.ts)
  • wittam-01: Feishu comment handler and dispatcher history includes comment event creation and later comment-session improvements in commits 1b94e8c and ebb72ba. (role: Feishu comment-flow contributor; confidence: high; commits: 1b94e8ca14de, ebb72baba3cd; files: extensions/feishu/src/comment-handler.ts, extensions/feishu/src/comment-dispatcher.ts)
  • vincentkoc: Recent Feishu dedup history includes commit 085d0c5, which is adjacent to the PR's dedup fallback change. (role: recent Feishu dedup contributor; confidence: medium; commits: 085d0c5d3006; files: extensions/feishu/src/dedup.ts)
  • m1heng: Feishu package metadata and README identify the Feishu/Lark plugin as community maintained by this handle. (role: community maintenance signal; confidence: medium; files: extensions/feishu/package.json, extensions/feishu/README.md)
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.

@openclaw-barnacle openclaw-barnacle Bot added 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. labels Jun 14, 2026
@qingminglong

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 14, 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.

Re-review progress:

@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. labels Jun 14, 2026
@qingminglong

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper clawsweeper Bot added the merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. label Jun 14, 2026
… global runtime

ClawSweeper flagged that createFeishuReplyDispatcher still called
getFeishuRuntime() from the global store instead of using the already-available
channel from bot.ts's core object. Thread channel as a direct parameter.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@qingminglong

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 14, 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.

Re-review progress:

The channel parameter type previously kept the full text and reply sub-types,
which the test mockChannel didn't satisfy. Narrow to exact methods used:
- text: resolveTextChunkLimit, resolveChunkMode, resolveMarkdownTableMode,
        convertMarkdownTables, chunkTextWithMode, chunkMarkdownTextWithMode
- reply: createReplyDispatcherWithTyping, resolveHumanDelayConfig
@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. labels Jun 14, 2026
@qingminglong

Copy link
Copy Markdown
Contributor Author

Have a nice day,All checks have passed,please help to merge!

@clawsweeper clawsweeper Bot added status: 🛠️ actively grinding The PR author has acted after the latest ClawSweeper review and work remains. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 16, 2026
@qingminglong

Copy link
Copy Markdown
Contributor Author

please close

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: supplied External PR includes structured after-fix real behavior proof. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: M status: 🛠️ actively grinding The PR author has acted after the latest ClawSweeper review and work remains.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant