openclaw - ✅(Solved) Fix Discord exec approval buttons spam 'Failed to submit' errors when elevated mode auto-resolves [1 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#66906Fetched 2026-04-16 06:37:23
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
0
Author
Timeline (top)
commented ×1cross-referenced ×1

Error Message

The approval card renders in Discord with interactive buttons, but by the time the user sees it, the approval is already consumed. Clicking any button produces an ephemeral error:

Problem 2: Multiple error messages per click

Each button click generates 3-5 failed exec.approval.resolve attempts from different gateway WebSocket connection IDs, each producing a separate ephemeral error message. This causes notification spam. 3. Deduplicate resolve attempts on the gateway side — if an approval ID is already consumed, silently ignore subsequent resolve calls instead of returning an error that triggers an ephemeral user-facing message 4. Suppress the ephemeral error in the Discord button handler when the approval was already resolved (treat "already resolved" as success, not failure) Option 4 is the simplest: in ExecApprovalButton.run(), treat a false return from resolveApproval() as a no-op rather than showing an error followUp. The approval succeeded — the user just clicked a stale button. Alternatively, check whether the error is specifically "already resolved" vs "unknown" and only show an error for truly unknown approval IDs. User sees this in DMs — three identical error ephemeral messages stacked:

Fix Action

Fixed

PR fix notes

PR #66918: Discord: ignore stale exec approval clicks

Description (problem / solution / changelog)

Summary

  • Problem: Discord exec approval buttons still surface a user-facing failure when the approval was already auto-resolved and the button is clicked late.
  • Why it matters: stale approval cards can generate confusing ephemeral errors even though the approval already succeeded, which makes elevated-mode auto-resolution look broken.
  • What changed: Discord now treats approval-not-found responses from the gateway as a benign stale-click outcome and suppresses the follow-up error; the nearby monitor tests now cover both generic failures and already-resolved approvals.
  • What did NOT change (scope boundary): this PR does not change gateway approval resolution semantics, Discord approval delivery/update behavior, or approval-card cleanup policy.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #66906
  • Related #66906
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: the Discord button handler treated every failed exec.approval.resolve attempt the same, including the expected approval-not-found case that happens when a stale button races with auto-resolution.
  • Missing detection / guardrail: the Discord monitor tests covered generic gateway failures but did not lock in the already-resolved approval case as a benign no-op.
  • Contributing context (if known): HEAD already updates or removes approval cards after resolution, but a late click can still reach the handler before Discord reflects that state change.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: extensions/discord/src/monitor/exec-approvals.test.ts
  • Scenario the test should lock in: a stale Discord exec approval click that reaches the gateway after the approval has already been resolved should acknowledge the interaction and not send an ephemeral failure follow-up.
  • Why this is the smallest reliable guardrail: the bug lives in the Discord button handler's error classification path, so the focused monitor helper test exercises the exact branch without requiring a live Discord/gateway repro.
  • Existing test that already covers this (if any): the file already covered the generic failed-resolution follow-up path.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

  • Late clicks on already-resolved Discord exec approval buttons no longer show the "Failed to submit approval decision" ephemeral error.
  • Real Discord approval submission failures still surface the existing error follow-up.

Diagram (if applicable)

Before:
[stale Discord approval click] -> [gateway returns approval not found] -> [ephemeral failure shown]

After:
[stale Discord approval click] -> [gateway returns approval not found] -> [interaction acknowledged] -> [no user-facing error]

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local repo checkout on main
  • Model/provider: N/A
  • Integration/channel (if any): Discord
  • Relevant config (redacted): Discord exec approvals enabled; issue repro uses session-level elevated=on

Steps

  1. Enable Discord exec approvals and run a session with auto-resolving elevated exec approvals.
  2. Let an exec approval auto-resolve, then click a stale Discord approval button.
  3. Observe the button handler outcome.

Expected

  • Already-resolved approval clicks are treated as stale/no-op interactions.
  • Only genuine submission failures show the ephemeral error.

Actual

  • Before this change, already-resolved approval clicks showed the same ephemeral failure as real submission errors.

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: reviewed the live Discord button handler path, confirmed the gateway still returns approval-not-found for stale ids, added a focused regression test for the stale-click branch, reran the targeted test file, and ran the full Discord extension lane.
  • Edge cases checked: generic gateway failures still show the existing ephemeral error; successful approval resolutions still return success; approval-not-found errors from the gateway resolver are now classified separately.
  • What you did not verify: I did not run a live Discord manual repro against a real gateway session.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

If a bot review conversation is addressed by this PR, resolve that conversation yourself. Do not leave bot review conversation cleanup for maintainers.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Risks and Mitigations

  • Risk: a genuine gateway failure could be accidentally treated as stale if the not-found classification is too broad.
    • Mitigation: the handler only suppresses errors that match the shared approval-not-found helper; all other failures still follow the existing error path.

Additional Notes

  • Per repo policy for user-facing bug fixes, no changelog entry was added for this narrowly scoped test-backed regression fix.

AI Assistance

  • AI-assisted
  • Testing level: scoped tests + full Discord extension lane
  • I reviewed the code changes and understand what they do

Made with Cursor

Changed files

  • extensions/discord/src/monitor/exec-approvals.test.ts (modified, +55/-7)
  • extensions/discord/src/monitor/exec-approvals.ts (modified, +42/-6)

Code Example

# Auto-approval succeeds (elevated=on):
21:13:04 [ws] ⇄ res ✓ exec.approval.waitDecision 3385ms conn=395aa190…dfc0

# Then 4 resolve attempts fail from different connections — all within ~1 second:
21:13:04 [ws] ⇄ res ✗ exec.approval.resolve 0ms errorCode=INVALID_REQUEST errorMessage=unknown or expired approval id conn=a84fb227…a9b7
21:13:04 [ws] ⇄ res ✗ exec.approval.resolve 0ms errorCode=INVALID_REQUEST errorMessage=unknown or expired approval id conn=1c380448…f14e
21:13:04 [ws] ⇄ res ✗ exec.approval.resolve 0ms errorCode=INVALID_REQUEST errorMessage=unknown or expired approval id conn=daf9e522…dd00
21:13:05 [ws] ⇄ res ✗ exec.approval.resolve 0ms errorCode=INVALID_REQUEST errorMessage=unknown or expired approval id conn=b1465452…58cf

---

VESSEL [APP]
Failed to submit approval decision for Allowed (once).
The request may have expired or already been resolved.
RAW_BUFFERClick to expand / collapse

Bug Description

When elevated=on is active for a session, exec approval requests are auto-resolved within 1-7 seconds. However, the Discord approval card (with Allow once / Allow always / Deny buttons) is still sent to the channel before auto-resolution completes.

This creates two user-facing problems:

Problem 1: Stale approval cards with clickable buttons

The approval card renders in Discord with interactive buttons, but by the time the user sees it, the approval is already consumed. Clicking any button produces an ephemeral error:

Failed to submit approval decision for Allowed (once). The request may have expired or already been resolved.

Problem 2: Multiple error messages per click

Each button click generates 3-5 failed exec.approval.resolve attempts from different gateway WebSocket connection IDs, each producing a separate ephemeral error message. This causes notification spam.

Gateway Log Evidence

# Auto-approval succeeds (elevated=on):
21:13:04 [ws] ⇄ res ✓ exec.approval.waitDecision 3385ms conn=395aa190…dfc0

# Then 4 resolve attempts fail from different connections — all within ~1 second:
21:13:04 [ws] ⇄ res ✗ exec.approval.resolve 0ms errorCode=INVALID_REQUEST errorMessage=unknown or expired approval id conn=a84fb227…a9b7
21:13:04 [ws] ⇄ res ✗ exec.approval.resolve 0ms errorCode=INVALID_REQUEST errorMessage=unknown or expired approval id conn=1c380448…f14e
21:13:04 [ws] ⇄ res ✗ exec.approval.resolve 0ms errorCode=INVALID_REQUEST errorMessage=unknown or expired approval id conn=daf9e522…dd00
21:13:05 [ws] ⇄ res ✗ exec.approval.resolve 0ms errorCode=INVALID_REQUEST errorMessage=unknown or expired approval id conn=b1465452…58cf

Environment

  • OpenClaw 2026.4.8
  • macOS, single Discord account, single shard
  • channels.discord.execApprovals.enabled = true
  • Session-level elevated=on

Expected Behavior

One or more of:

  1. Don't send the Discord approval card when elevated mode will auto-resolve the approval
  2. Update/dismiss the approval card after auto-resolution (edit the message to show "Auto-approved (elevated)" and remove buttons)
  3. Deduplicate resolve attempts on the gateway side — if an approval ID is already consumed, silently ignore subsequent resolve calls instead of returning an error that triggers an ephemeral user-facing message
  4. Suppress the ephemeral error in the Discord button handler when the approval was already resolved (treat "already resolved" as success, not failure)

Suggested Fix

Option 4 is the simplest: in ExecApprovalButton.run(), treat a false return from resolveApproval() as a no-op rather than showing an error followUp. The approval succeeded — the user just clicked a stale button. Alternatively, check whether the error is specifically "already resolved" vs "unknown" and only show an error for truly unknown approval IDs.

Option 2 would be the best UX: after auto-resolution, edit the original approval card message to indicate it was auto-approved and remove the interactive buttons.

Screenshot

User sees this in DMs — three identical error ephemeral messages stacked:

VESSEL [APP]
Failed to submit approval decision for Allowed (once).
The request may have expired or already been resolved.

Repeated 3x for a single button click.

extent analysis

TL;DR

Implement a check in ExecApprovalButton.run() to treat a false return from resolveApproval() as a no-op when the error is "already resolved", rather than showing an error message.

Guidance

  • Review the ExecApprovalButton.run() function to determine how it currently handles the return value of resolveApproval() and modify it to silently ignore "already resolved" errors.
  • Consider adding a check for the specific error message "already resolved" to distinguish it from "unknown" approval IDs and only show an error for the latter.
  • Evaluate the feasibility of implementing Option 2 from the Expected Behavior section, which involves editing the original approval card message after auto-resolution to indicate it was auto-approved and remove interactive buttons.
  • Investigate the gateway log evidence to understand the sequence of events leading to the multiple failed exec.approval.resolve attempts and determine if there are any opportunities to deduplicate resolve attempts on the gateway side.

Example

// Pseudocode example of the suggested fix in ExecApprovalButton.run()
if (!resolveApproval()) {
  const errorMessage = getErrorMessage();
  if (errorMessage === "already resolved") {
    // Silently ignore "already resolved" errors
    return;
  } else {
    // Show an error message for truly unknown approval IDs
    showErrorMessage(errorMessage);
  }
}

Notes

The provided solution focuses on treating "already resolved" errors as a no-op, but a more comprehensive fix might involve addressing the root cause of the multiple failed exec.approval.resolve attempts and the stale approval cards.

Recommendation

Apply the suggested workaround by modifying ExecApprovalButton.run() to treat "already resolved" errors as a no-op, as this is the simplest and most straightforward solution to mitigate the user-facing issues.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix Discord exec approval buttons spam 'Failed to submit' errors when elevated mode auto-resolves [1 pull requests, 1 comments, 2 participants]