Skip to content

fix(discord): strip channel: prefix in resolveChannelIdForBinding to fix thread_binding_invalid on ACP spawn#63354

Open
sam-bot-hbtu9992 wants to merge 2 commits intoopenclaw:mainfrom
sam-bot-hbtu9992:fix/discord-thread-binding-channel-prefix
Open

fix(discord): strip channel: prefix in resolveChannelIdForBinding to fix thread_binding_invalid on ACP spawn#63354
sam-bot-hbtu9992 wants to merge 2 commits intoopenclaw:mainfrom
sam-bot-hbtu9992:fix/discord-thread-binding-channel-prefix

Conversation

@sam-bot-hbtu9992
Copy link
Copy Markdown

Summary

Fixes the thread_binding_invalid error when spawning ACP sessions with thread: true from Discord guild channels after upgrading to 2026.4.5+.

Fixes #63329

Root Cause

In resolveChannelIdForBinding, the params.threadId is passed directly to Routes.channel(). After the ACPX runtime refactor in #61319 (2026.4.5), the conversation ID is now passed in OpenClaw's internal prefixed format (channel:1490136404385075452) rather than as a raw numeric Discord ID.

Routes.channel() (from discord-api-types) URL-encodes the colon in the channel: prefix, producing /channels/channel%3A1490136404385075452. Discord returns an error for this invalid path. The error is caught and swallowed silently, causing resolveChannelIdForBinding to return null, bindTarget to return null, and the spawn to fail with thread_binding_invalid.

Fix

Strip the channel: or user: prefix from params.threadId before passing it to Routes.channel(), so the Discord REST API always receives a valid numeric channel ID.

- const channel = (await rest.get(Routes.channel(params.threadId))) as {
+ const channel = (await rest.get(Routes.channel(params.threadId.replace(/^(channel:|user:)/i, '')))) as {

Testing

Verified locally by applying the equivalent patch to the built bundle (/app/dist/thread-bindings.discord-api-CL8HMdV4.js). After the patch, sessions_spawn with thread: true from a Discord guild text channel succeeds and creates a thread as expected.

Notes

  • Regression introduced in 2026.4.5 by the ACPX runtime embed refactor ([codex] Embed ACP runtime in plugin #61319)
  • Worked correctly on 2026.4.1 and earlier
  • An alternative fix would be to use the existing resolveDiscordChannelId() helper which already handles prefix stripping

…ore Discord API call

Fixes openclaw#63329

When spawning an ACP session with thread=true from a Discord guild channel,
resolveChannelIdForBinding receives the conversation ID in OpenClaw's internal
prefixed format (channel:1490136404385075452) and passes it directly to
Routes.channel(). This causes Routes.channel() to URL-encode the colon,
producing /channels/channel%3A1490136404385075452, which Discord rejects with
an error. The error is swallowed silently, resolveChannelIdForBinding returns
null, bindTarget returns null, and the spawn fails with thread_binding_invalid.

Fix: strip the channel: or user: prefix from params.threadId before passing
it to Routes.channel() so the Discord REST API receives a valid numeric ID.
@openclaw-barnacle openclaw-barnacle Bot added channel: discord Channel integration: discord size: XS labels Apr 8, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 8, 2026

Greptile Summary

Single-line regression fix in resolveChannelIdForBinding: strips the channel:/user: OpenClaw internal prefix from params.threadId before passing it to Routes.channel(), preventing URL-encoded colons from producing an invalid Discord API path. The fix correctly targets the 2026.4.5 regression where ACPX runtime started passing conversation IDs in prefixed format.

Confidence Score: 5/5

Safe to merge — targeted one-line fix with no logic changes beyond stripping an internal ID prefix.

The only finding is a P2 style suggestion to use an existing helper; the inline regex fix is functionally correct and directly addresses the reported regression. No blocking issues.

No files require special attention.

Vulnerabilities

No security concerns identified. The change strips a known internal prefix before a REST API call; no new input surfaces, secrets, or trust boundaries are affected.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/discord/src/monitor/thread-bindings.discord-api.ts
Line: 248

Comment:
**Consider using the existing `resolveDiscordChannelId` helper**

The file already imports helpers from elsewhere in the extension; the codebase has a purpose-built `resolveDiscordChannelId` in `src/target-parsing.ts` (re-exported via `targets.ts`) that already handles the `channel:` prefix-stripping correctly. Using it here would be more consistent with how the rest of the extension strips prefixes.

One caveat: `resolveDiscordChannelId` throws on a `user:` prefix (see its test), so the try/catch here would catch that and return `null` — effectively the same end result as the current inline regex for user-prefixed inputs. Either approach is correct for the stated regression; using the shared helper just keeps the stripping logic centralized.

```suggestion
    const channel = (await rest.get(Routes.channel(resolveDiscordChannelId(params.threadId)))) as {
```

If you prefer the inline regex (to avoid importing and to tolerate `user:` targets silently), that's also fine — the fix is functionally correct for the `channel:` case.

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

Reviews (1): Last reviewed commit: "fix(discord): strip channel: prefix in r..." | Re-trigger Greptile

params.cfg,
).rest;
const channel = (await rest.get(Routes.channel(params.threadId))) as {
const channel = (await rest.get(Routes.channel(params.threadId.replace(/^(channel:|user:)/i, '')))) as {
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 Consider using the existing resolveDiscordChannelId helper

The file already imports helpers from elsewhere in the extension; the codebase has a purpose-built resolveDiscordChannelId in src/target-parsing.ts (re-exported via targets.ts) that already handles the channel: prefix-stripping correctly. Using it here would be more consistent with how the rest of the extension strips prefixes.

One caveat: resolveDiscordChannelId throws on a user: prefix (see its test), so the try/catch here would catch that and return null — effectively the same end result as the current inline regex for user-prefixed inputs. Either approach is correct for the stated regression; using the shared helper just keeps the stripping logic centralized.

Suggested change
const channel = (await rest.get(Routes.channel(params.threadId.replace(/^(channel:|user:)/i, '')))) as {
const channel = (await rest.get(Routes.channel(resolveDiscordChannelId(params.threadId)))) as {

If you prefer the inline regex (to avoid importing and to tolerate user: targets silently), that's also fine — the fix is functionally correct for the channel: case.

Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/discord/src/monitor/thread-bindings.discord-api.ts
Line: 248

Comment:
**Consider using the existing `resolveDiscordChannelId` helper**

The file already imports helpers from elsewhere in the extension; the codebase has a purpose-built `resolveDiscordChannelId` in `src/target-parsing.ts` (re-exported via `targets.ts`) that already handles the `channel:` prefix-stripping correctly. Using it here would be more consistent with how the rest of the extension strips prefixes.

One caveat: `resolveDiscordChannelId` throws on a `user:` prefix (see its test), so the try/catch here would catch that and return `null` — effectively the same end result as the current inline regex for user-prefixed inputs. Either approach is correct for the stated regression; using the shared helper just keeps the stripping logic centralized.

```suggestion
    const channel = (await rest.get(Routes.channel(resolveDiscordChannelId(params.threadId)))) as {
```

If you prefer the inline regex (to avoid importing and to tolerate `user:` targets silently), that's also fine — the fix is functionally correct for the `channel:` case.

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

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good call — updated to use resolveDiscordChannelId from target-parsing.ts in the latest commit. Agreed it's more consistent with the rest of the codebase.

Addresses code review suggestion from greptile-apps bot.
Uses the existing resolveDiscordChannelId helper from target-parsing.ts
which is the idiomatic way to strip channel:/user: prefixes in this codebase.

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

Labels

channel: discord Channel integration: discord size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Discord: ACP thread spawn fails with thread_binding_invalid after 2026.4.5 (channel: prefix not stripped in resolveChannelIdForBinding)

1 participant