Skip to content

fix(feishu): resolve correct accountId for subagent group replies#58228

Closed
EronFan wants to merge 3 commits into
openclaw:mainfrom
EronFan:fix/feishu-multi-agent-account-resolution
Closed

fix(feishu): resolve correct accountId for subagent group replies#58228
EronFan wants to merge 3 commits into
openclaw:mainfrom
EronFan:fix/feishu-multi-agent-account-resolution

Conversation

@EronFan

@EronFan EronFan commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Fixes #58107

Problem
When multiple Feishu group agents (xixi, ling, aoao, weiwei) send messages in a group chat, only the main agent's messages are delivered correctly. Subagents were using the wrong accountId ('default') instead of their assigned accountId.

Root Cause
The Feishu channel's handleAction function was using ctx.accountId which may be undefined or default for subagents. Subagents have their own agentAccountId in the tool execution context, but this wasn't being used.

Solution
Modified extensions/feishu/src/channel.ts to prioritize agentAccountId (available for subagents) over ctx.accountId when resolving the account to use for sending messages.

Changes

  • In handleAction function, added logic to use agentAccountId from context when available
  • Updated all references to ctx.accountId within the function to use the effective accountId
  • This ensures subagents like xixi, ling, aoao, weiwei use their own Feishu accounts when sending group messages

Testing
The fix ensures that:

  1. Main agent continues to work as before
  2. Subagents use their assigned accountId (xixi, ling, aoao, weiwei) instead of default
  3. All Feishu message sending actions (send, thread-reply, reactions, etc.) respect the agent's accountId

aoao added 3 commits March 31, 2026 10:27
… flag

Before writing autoLastSuccessVersion, parse afterVersion from the JSON
stdout of 'openclaw update --json' and verify it matches the resolved
version AND differs from the current running VERSION.

This prevents a crash loop where:
1. npm install exits 0 but binary is not actually updated
2. autoLastSuccessVersion is written anyway
3. Gateway restarts, plugin version checks fail due to
   minGatewayVersion mismatch, all plugins skip loading

fix openclaw#58041
Fixes openclaw#58107 by ensuring subagents use their own accountId when sending
Feishu group messages. Previously, subagents would use the default
accountId ('default') instead of their assigned accountId, causing
messages to be sent from the wrong account.

The fix prioritizes agentAccountId from the tool execution context
(available for subagents) over the channel's accountId.
@openclaw-barnacle openclaw-barnacle Bot added channel: feishu Channel integration: feishu size: XL r: too-many-prs Auto-close: author has more than twenty active PRs. labels Mar 31, 2026
@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because the author has more than 10 active PRs in this repo. Please reduce the active PR queue and reopen or resubmit once it is back under the limit. You can close your own PRs to get back under the limit.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 74c9cca7e4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const { monitorFeishuProvider } = await import("./monitor.js");
const account = resolveFeishuRuntimeAccount(
{ cfg: ctx.cfg, accountId: ctx.accountId },
{ cfg: ctx.cfg, accountId: effectiveAccountId },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Restore ctx.accountId usage in Feishu gateway startup

gateway.startAccount now references effectiveAccountId, but that identifier is only declared inside actions.handleAction and is out of scope here. When Feishu startup reaches this line, it will throw a ReferenceError and prevent the Feishu account monitor from starting. This regresses all Feishu runtime startup paths, not just subagent message actions.

Useful? React with 👍 / 👎.


// Parse version from output (format: "openclaw 2026.3.28")
const versionOutput = versionCheckStep.stdoutTail || "";
const versionMatch = versionOutput.match(/openclaw\s+(\d+\.\d+\.\d+)/);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Parse version output using the actual CLI version format

The version check parses --version output with a case-sensitive pattern openclaw\s+(\d+\.\d+\.\d+), but the CLI emits OpenClaw <version> (see src/cli/program/help.ts), and may include prerelease/commit suffixes. In those normal cases versionMatch is null, so git updates fail with version-parse-failed before running doctor, even when the updated checkout is healthy.

Useful? React with 👍 / 👎.

@greptile-apps

greptile-apps Bot commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR bundles three concerns: (1) the stated Feishu fix for subagent accountId resolution, (2) an accidental backup file commit, and (3) unrelated auto-update reliability changes (update-runner.ts, update-startup.ts).

The Feishu fix itself (handleAction using effectiveAccountId) is correct for all the action handlers, but it introduces a critical scoping bug: effectiveAccountId is declared inside handleAction and then referenced in the sibling startAccount function where it is not in scope. This will produce a TypeScript compilation error and a runtime ReferenceError whenever startAccount is called.

The auto-update changes in update-startup.ts introduce a regression: the new success condition parses parsed.afterVersion from the update command's JSON output, but the actual output field is parsed.after.version. Because the key is always missing, afterVersion is always null, so autoLastSuccessVersion is never written and the system will re-attempt every successful update indefinitely.

Key issues:

  • startAccount references effectiveAccountId from a different function's scope (P1 — compile/runtime error)
  • update-startup.ts parses the wrong JSON key (parsed.afterVersion vs parsed.after.version), breaking update-success tracking (P1 — behavioral regression)
  • channel.ts.backup is a development artefact that should not be in version control (P1 — cleanup)
  • (ctx as any).agentAccountId bypasses TypeScript type safety; agentAccountId should be added to the context type (P2)

Confidence Score: 2/5

Not safe to merge: two P1 correctness bugs (scope error in startAccount, wrong JSON key for afterVersion) and a committed backup file need to be resolved first.

Three P1 issues remain: the effectiveAccountId out-of-scope reference will cause a TypeScript compile error and/or a runtime ReferenceError in startAccount; the parsed.afterVersion vs parsed.after.version mismatch silently breaks auto-update success tracking; and the .backup file is an unintended artefact. These are present defects on the changed path that block merge.

extensions/feishu/src/channel.ts (startAccount scope bug), src/infra/update-startup.ts (wrong JSON key), extensions/feishu/src/channel.ts.backup (should be deleted)

Important Files Changed

Filename Overview
extensions/feishu/src/channel.ts Core Feishu fix: effectiveAccountId correctly introduced in handleAction but mistakenly referenced in sibling startAccount where it is out of scope (compile error / runtime ReferenceError). Also uses (ctx as any) for the new agentAccountId field.
extensions/feishu/src/channel.ts.backup Development artefact (mid-edit snapshot of channel.ts) accidentally committed. Should be deleted before merge.
src/infra/update-runner.ts Unrelated to the PR's stated Feishu fix: adds a pre-flight binary/package-version consistency check before running doctor --fix during git-mode updates, guarding against issue #58041. Logic looks correct in isolation.
src/infra/update-startup.ts Unrelated to the Feishu fix; tightens the auto-update success condition but parses the version from the wrong JSON key (parsed.afterVersion instead of parsed.after.version), meaning autoLastSuccessVersion is never recorded and updates are retried indefinitely.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/feishu/src/channel.ts
Line: 1140-1152

Comment:
**`effectiveAccountId` is out of scope here — will cause a `ReferenceError` at runtime**

`effectiveAccountId` is declared with `const` inside the `handleAction` callback at line 621. The `startAccount` callback (here) is a sibling property of a different sub-object (`gateway`), not nested inside `handleAction`, so it cannot close over that variable. TypeScript should flag this as a compile error and, if somehow bypassed, calling `startAccount` will throw `ReferenceError: effectiveAccountId is not defined`.

Each usage on lines 1140, 1144, 1146, and 1152 needs to resolve the effective account directly within this function:

```suggestion
          const account = resolveFeishuRuntimeAccount(
            { cfg: ctx.cfg, accountId: (ctx as any).agentAccountId ?? ctx.accountId },
            { requireEventSecrets: true },
          );
          const port = account.config?.webhookPort ?? null;
          const startEffectiveAccountId = (ctx as any).agentAccountId ?? ctx.accountId;
          ctx.setStatus({ accountId: startEffectiveAccountId, port });
          ctx.log?.info(
            `starting feishu[${startEffectiveAccountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
          );
          return monitorFeishuProvider({
            config: ctx.cfg,
            runtime: ctx.runtime,
            abortSignal: ctx.abortSignal,
            accountId: startEffectiveAccountId,
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/infra/update-startup.ts
Line: 465-466

Comment:
**Wrong JSON field path — `afterVersion` will always be `null`**

The update command outputs a JSON object where the version is nested at `after.version` (e.g. `{ "after": { "version": "2026.3.28" } }`), not at a top-level `afterVersion` key. As a result `parsed.afterVersion` is always `undefined`, `afterVersion` is always `null`, the `if (outcome.ok && afterVersion != null …)` branch is never taken, and `autoLastSuccessVersion` is never written. Every successful auto-update will be logged as "auto-update skipped (version mismatch or unchanged)" and the system will keep retrying on every subsequent check.

The field access should be:
```suggestion
              if (parsed && parsed.after && typeof parsed.after.version === "string") {
                return parsed.after.version;
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/feishu/src/channel.ts.backup
Line: 1

Comment:
**Backup file should not be committed to the repository**

`channel.ts.backup` is a development artefact (a mid-edit snapshot of `channel.ts`) and has no place in version control. It adds ~1,190 lines of near-duplicate code that will cause confusion and will never be kept up to date. Please remove this file before merging.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: extensions/feishu/src/channel.ts
Line: 621

Comment:
**`(ctx as any)` bypasses TypeScript's type safety**

Accessing `agentAccountId` via `(ctx as any)` sidesteps type checking and makes it invisible to refactoring tools. If `agentAccountId` is a legitimate field that subagent contexts are expected to carry, it should be added to the relevant context type (or a subtype/intersection) so that access is type-safe and the intent is self-documenting.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "chore: update runner file" | Re-trigger Greptile

Comment on lines +1140 to +1152
{ cfg: ctx.cfg, accountId: effectiveAccountId },
{ requireEventSecrets: true },
);
const port = account.config?.webhookPort ?? null;
ctx.setStatus({ accountId: ctx.accountId, port });
ctx.setStatus({ accountId: effectiveAccountId, port });
ctx.log?.info(
`starting feishu[${ctx.accountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
`starting feishu[${effectiveAccountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
);
return monitorFeishuProvider({
config: ctx.cfg,
runtime: ctx.runtime,
abortSignal: ctx.abortSignal,
accountId: ctx.accountId,
accountId: effectiveAccountId,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 effectiveAccountId is out of scope here — will cause a ReferenceError at runtime

effectiveAccountId is declared with const inside the handleAction callback at line 621. The startAccount callback (here) is a sibling property of a different sub-object (gateway), not nested inside handleAction, so it cannot close over that variable. TypeScript should flag this as a compile error and, if somehow bypassed, calling startAccount will throw ReferenceError: effectiveAccountId is not defined.

Each usage on lines 1140, 1144, 1146, and 1152 needs to resolve the effective account directly within this function:

Suggested change
{ cfg: ctx.cfg, accountId: effectiveAccountId },
{ requireEventSecrets: true },
);
const port = account.config?.webhookPort ?? null;
ctx.setStatus({ accountId: ctx.accountId, port });
ctx.setStatus({ accountId: effectiveAccountId, port });
ctx.log?.info(
`starting feishu[${ctx.accountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
`starting feishu[${effectiveAccountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
);
return monitorFeishuProvider({
config: ctx.cfg,
runtime: ctx.runtime,
abortSignal: ctx.abortSignal,
accountId: ctx.accountId,
accountId: effectiveAccountId,
const account = resolveFeishuRuntimeAccount(
{ cfg: ctx.cfg, accountId: (ctx as any).agentAccountId ?? ctx.accountId },
{ requireEventSecrets: true },
);
const port = account.config?.webhookPort ?? null;
const startEffectiveAccountId = (ctx as any).agentAccountId ?? ctx.accountId;
ctx.setStatus({ accountId: startEffectiveAccountId, port });
ctx.log?.info(
`starting feishu[${startEffectiveAccountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
);
return monitorFeishuProvider({
config: ctx.cfg,
runtime: ctx.runtime,
abortSignal: ctx.abortSignal,
accountId: startEffectiveAccountId,
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/channel.ts
Line: 1140-1152

Comment:
**`effectiveAccountId` is out of scope here — will cause a `ReferenceError` at runtime**

`effectiveAccountId` is declared with `const` inside the `handleAction` callback at line 621. The `startAccount` callback (here) is a sibling property of a different sub-object (`gateway`), not nested inside `handleAction`, so it cannot close over that variable. TypeScript should flag this as a compile error and, if somehow bypassed, calling `startAccount` will throw `ReferenceError: effectiveAccountId is not defined`.

Each usage on lines 1140, 1144, 1146, and 1152 needs to resolve the effective account directly within this function:

```suggestion
          const account = resolveFeishuRuntimeAccount(
            { cfg: ctx.cfg, accountId: (ctx as any).agentAccountId ?? ctx.accountId },
            { requireEventSecrets: true },
          );
          const port = account.config?.webhookPort ?? null;
          const startEffectiveAccountId = (ctx as any).agentAccountId ?? ctx.accountId;
          ctx.setStatus({ accountId: startEffectiveAccountId, port });
          ctx.log?.info(
            `starting feishu[${startEffectiveAccountId}] (mode: ${account.config?.connectionMode ?? "websocket"})`,
          );
          return monitorFeishuProvider({
            config: ctx.cfg,
            runtime: ctx.runtime,
            abortSignal: ctx.abortSignal,
            accountId: startEffectiveAccountId,
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +465 to +466
if (parsed && typeof parsed.afterVersion === "string") {
return parsed.afterVersion;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Wrong JSON field path — afterVersion will always be null

The update command outputs a JSON object where the version is nested at after.version (e.g. { "after": { "version": "2026.3.28" } }), not at a top-level afterVersion key. As a result parsed.afterVersion is always undefined, afterVersion is always null, the if (outcome.ok && afterVersion != null …) branch is never taken, and autoLastSuccessVersion is never written. Every successful auto-update will be logged as "auto-update skipped (version mismatch or unchanged)" and the system will keep retrying on every subsequent check.

The field access should be:

Suggested change
if (parsed && typeof parsed.afterVersion === "string") {
return parsed.afterVersion;
if (parsed && parsed.after && typeof parsed.after.version === "string") {
return parsed.after.version;
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/update-startup.ts
Line: 465-466

Comment:
**Wrong JSON field path — `afterVersion` will always be `null`**

The update command outputs a JSON object where the version is nested at `after.version` (e.g. `{ "after": { "version": "2026.3.28" } }`), not at a top-level `afterVersion` key. As a result `parsed.afterVersion` is always `undefined`, `afterVersion` is always `null`, the `if (outcome.ok && afterVersion != null …)` branch is never taken, and `autoLastSuccessVersion` is never written. Every successful auto-update will be logged as "auto-update skipped (version mismatch or unchanged)" and the system will keep retrying on every subsequent check.

The field access should be:
```suggestion
              if (parsed && parsed.after && typeof parsed.after.version === "string") {
                return parsed.after.version;
```

How can I resolve this? If you propose a fix, please make it concise.

@@ -0,0 +1,1190 @@
import { describeAccountSnapshot } from "openclaw/plugin-sdk/account-helpers";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Backup file should not be committed to the repository

channel.ts.backup is a development artefact (a mid-edit snapshot of channel.ts) and has no place in version control. It adds ~1,190 lines of near-duplicate code that will cause confusion and will never be kept up to date. Please remove this file before merging.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/channel.ts.backup
Line: 1

Comment:
**Backup file should not be committed to the repository**

`channel.ts.backup` is a development artefact (a mid-edit snapshot of `channel.ts`) and has no place in version control. It adds ~1,190 lines of near-duplicate code that will cause confusion and will never be kept up to date. Please remove this file before merging.

How can I resolve this? If you propose a fix, please make it concise.

describeMessageTool: describeFeishuMessageTool,
handleAction: async (ctx) => {
// Use agentAccountId if available (for subagents), otherwise fall back to ctx.accountId
const effectiveAccountId = (ctx as any).agentAccountId ?? ctx.accountId ?? undefined;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 (ctx as any) bypasses TypeScript's type safety

Accessing agentAccountId via (ctx as any) sidesteps type checking and makes it invisible to refactoring tools. If agentAccountId is a legitimate field that subagent contexts are expected to carry, it should be added to the relevant context type (or a subtype/intersection) so that access is type-safe and the intent is self-documenting.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/feishu/src/channel.ts
Line: 621

Comment:
**`(ctx as any)` bypasses TypeScript's type safety**

Accessing `agentAccountId` via `(ctx as any)` sidesteps type checking and makes it invisible to refactoring tools. If `agentAccountId` is a legitimate field that subagent contexts are expected to carry, it should be added to the relevant context type (or a subtype/intersection) so that access is type-safe and the intent is self-documenting.

How can I resolve this? If you propose a fix, please make it concise.

EronFan added a commit to EronFan/openclaw-fork that referenced this pull request Apr 4, 2026
…kup removal)

1. Remove channel.ts.backup development artefact
2. Fix effectiveAccountId scope error in startAccount: declare local
   startEffectiveAccountId instead of referencing out-of-scope variable
3. Fix update-startup.ts JSON path: parsed.afterVersion ->
   parsed.after.version
EronFan added a commit to EronFan/openclaw-fork that referenced this pull request Apr 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: feishu Channel integration: feishu r: too-many-prs Auto-close: author has more than twenty active PRs. size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Multiple Feishu group agents - only main reply delivered, other agents' replies silently dropped

1 participant