Skip to content

fix(feishu): decode URL-encoded filenames when receiving files#43916

Closed
ccsang wants to merge 12 commits into
openclaw:mainfrom
ccsang:fix/issue-43840-feishu-filename-encoding
Closed

fix(feishu): decode URL-encoded filenames when receiving files#43916
ccsang wants to merge 12 commits into
openclaw:mainfrom
ccsang:fix/issue-43840-feishu-filename-encoding

Conversation

@ccsang

@ccsang ccsang commented Mar 12, 2026

Copy link
Copy Markdown

Fixes #43840

Summary

When sending files with non-ASCII filenames (Chinese, Japanese, emoji), the filename is URL-encoded via sanitizeFileNameForUpload for safe multipart/form-data transmission. However, when receiving files from Feishu messages, the filename was not decoded, causing display issues like %E6%B5%8B%E8%AF%95.txt instead of 测试.txt.

Changes

  • Added decodeFileNameFromFeishu() function to decode URL-encoded filenames
  • Applied decoding in parseMediaKeys() and formatSubMessageContent() when parsing Feishu message content
  • Added test cases for the new decode function

Test Plan

  • Added 8 test cases for decodeFileNameFromFeishu function
  • Lint passes
  • Manual verification: send file with Chinese filename in Feishu, verify display shows decoded name

@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui channel: feishu Channel integration: feishu size: M labels Mar 12, 2026
@greptile-apps

greptile-apps Bot commented Mar 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes non-ASCII filename display in Feishu by adding decodeFileNameFromFeishu to reverse the URL-encoding applied during upload and applying it when parsing incoming file messages. It also ships an unrelated but useful UI improvement that pairs tool-call args with result cards so the sidebar shows the full command and arguments alongside output.

Key findings:

  • Logic bug — double queue shift in tool-cards.ts (lines 65–84): When no toolCallId is present and a result card has no own args, the code calls queue.shift() to retrieve the paired args, then unconditionally calls queue.shift() a second time in the "always consume" block. This consumes two entries per result instead of one, causing subsequent same-named results to receive mismatched args.
  • Type signature mismatch in decodeFileNameFromFeishu: The function is declared (fileName: string): string but the early-return passes null/undefined through unchanged — the tests confirm this with as any casts. The signature should be string | null | undefined → string | null | undefined to be accurate.
  • Stray * in JSDoc comment on decodeFileNameFromFeishu — cosmetic but should be cleaned up.
  • The core Feishu filename decode logic is correct and well-tested with 8 cases covering all expected edge cases.

Confidence Score: 2/5

  • The Feishu filename fix is safe to merge, but the tool-cards queue logic has a double-shift bug that will cause incorrect arg pairing in multi-call scenarios.
  • The primary Feishu change (the stated goal of the PR) is correct and well-covered by tests. However, the broader UI change in tool-cards.ts introduces a logic error in queue consumption: the queue-fallback branch already calls shift() to fetch args, and the unconditional "always consume" block immediately shifts again, skipping entries when multiple same-named tool results appear without toolCallIds. This bug is absent from the new tests (which only exercise single-call cases), making it easy to miss until it surfaces in production with multi-step agents.
  • ui/src/ui/chat/tool-cards.ts — the double-shift in the result-card arg-pairing loop requires attention before merge.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: ui/src/ui/chat/tool-cards.ts
Line: 65-84

Comment:
**Double-shift consumes two queue entries per queue-fallback result**

When `args` is `null`/`undefined` and the queue-fallback branch is taken (line 70: `args = queue.shift()`), the "always consume" block immediately shifts the queue a second time (line 82: `queue.shift()`). This means two entries are consumed for a single result card whenever the queue-fallback path is used.

Concretely: if two same-named tool calls exist and their result blocks carry no `toolCallId` and no own args, the second result will incorrectly receive the *third* call's args (or `undefined` if there is no third call).

The "always consume" block is only needed when the first block matched via `toolCallId` (which does not shift) or when the result already has its own args (which also does not shift). For the queue-fallback path, the shift was already performed on line 70 and should not be repeated.

Fix — only run the "always consume" block when the queue-fallback path was *not* already taken:

```
    // Always consume matching queue entry to keep queue in sync
    // Skip if we already consumed via queue fallback above
    const usedQueueFallback = (args !== undefined && args !== null) &&
      !(toolCallId && argsByToolCallId.has(toolCallId)) &&
      !(coerceArgs(item.arguments ?? item.args) !== undefined && coerceArgs(item.arguments ?? item.args) !== null);
```

Or more clearly, track whether the first block already called `shift`:

```typescript
    let args = coerceArgs(item.arguments ?? item.args);
    const toolCallId = ...;
    let usedQueueFallback = false;
    if (toolCallId && argsByToolCallId.has(toolCallId)) {
      args = argsByToolCallId.get(toolCallId);
    } else if (args === undefined || args === null) {
      if (argsQueueByName.has(name)) {
        const queue = argsQueueByName.get(name)!;
        if (queue.length > 0) {
          args = queue.shift();
          usedQueueFallback = true;
        }
      }
    }
    // Consume queue entry to keep it in sync — but only if we haven't already done so
    if (!usedQueueFallback && argsQueueByName.has(name)) {
      const queue = argsQueueByName.get(name)!;
      if (queue.length > 0) {
        queue.shift();
      }
    }
```

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/media.ts
Line: 243-258

Comment:
**Return type does not match implementation**

The function is declared as `(fileName: string): string`, but the early-return on line 244 returns `fileName` as-is — meaning `null` or `undefined` will be returned when a falsy non-string value is passed. The tests confirm this by using `as any` to bypass the type checker.

The callers pass `parsed.file_name` (JSON-parsed `any`), so `null`/`undefined` is a realistic input. The type signature should reflect this to avoid silent type errors elsewhere:

```suggestion
export function decodeFileNameFromFeishu(fileName: string | null | undefined): string | null | undefined {
```

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/media.ts
Line: 241

Comment:
**Stray asterisk in JSDoc comment**

There is an extra `*` mid-sentence, likely a copy-paste artifact.

```suggestion
 * This is used when receiving filenames from Feishu messages
 * to ensure proper display of non-ASCII characters.
```

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

Last reviewed commit: 3250356

Comment thread ui/src/ui/chat/tool-cards.ts
Comment thread extensions/feishu/src/media.ts Outdated
Comment thread extensions/feishu/src/media.ts Outdated

@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: 325035697f

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/src/ui/chat/tool-cards.ts

@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: b14ebd1255

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread extensions/feishu/src/media.ts Outdated

@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: 7118d3fa3e

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread extensions/feishu/src/media.ts Outdated

@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: e9e468dd29

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/src/ui/chat/tool-cards.ts
Comment thread extensions/feishu/src/media.ts
ccsang added 12 commits March 12, 2026 11:43
- Add formatToolArgsForSidebar to render untruncated command and sibling args
- Preserve truncated in-card detail while sidebar shows full fidelity
- Sidebar now includes Command section plus Args section when extra fields exist
- Add browser tests for sidebar content (full command, args, no-output case)

Closes openclaw#43153
- Defer args serialization until sidebar is opened (P2)
- Populate result-card args from paired tool call (P1)
- Choose fence length from longest backtick run (P2)
- Keep markdown fences at minimum 3 backticks (P2)
- Use FIFO queue for same-name tool call arg pairing (P2)
When sending files with non-ASCII filenames (Chinese, Japanese, emoji),
the filename is URL-encoded via sanitizeFileNameForUpload for safe
multipart/form-data transmission. However, when receiving files from
Feishu messages, the filename was not decoded, causing display issues
like '%E6%B5%8B%E8%AF%95.txt' instead of '测试.txt'.

This PR adds decodeFileNameFromFeishu() to decode URL-encoded filenames
when parsing Feishu message content, ensuring proper display of non-ASCII
filenames in the [File: ...] placeholder and mediaKeys.fileName field.

Fixes openclaw#43840
- Use more precise regex to only match valid percent-encoded sequences
- Avoids false positives for filenames like report-100%.pdf
Return type now matches implementation (string | null | undefined)

Only decode UTF-8 percent-encoded sequences to avoid false positives

Add test for literal percent sequences
@ccsang ccsang force-pushed the fix/issue-43840-feishu-filename-encoding branch from e9e468d to e11efb9 Compare March 12, 2026 11:44

@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: e11efb9b4d

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ui/src/ui/chat/tool-cards.ts
@ccsang

ccsang commented Mar 19, 2026

Copy link
Copy Markdown
Author

Closing due to extensive merge conflicts (500+ files). This PR mixed UI and feishu changes that became difficult to reconcile with main branch refactoring.

The feishu filename decoding fix can be re-submitted as a clean, focused PR if needed.

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

Labels

app: web-ui App: web-ui channel: feishu Channel integration: feishu size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Filename Becomes URL-Encoded String After Sending Files in Lark/Feishu

1 participant