Skip to content

[Bug] AI Card stop button does not abort the underlying embedded agent run #531

@nautybit

Description

@nautybit

Bug Description

When a user clicks the "停止" (stop) button on an AI Card in DingTalk channel, only the card streaming is stopped (finalize is skipped). The underlying embedded agent run continues running in the background until it hits the full timeoutSeconds (120s by default).

Steps to Reproduce

  1. Send a message to the bot that triggers a long-running agent run
  2. While the AI Card is streaming, click the stop button
  3. The card stops, but the embedded run continues
  4. The run only terminates when it times out (after 120s by default)

Expected vs Actual Behavior

Expected: Stop button should abort both the card streaming AND the underlying embedded agent run, similar to the /stop CLI command.

Actual: Only the card is stopped. The embedded run continues until natural timeout.

Evidence

Gateway logs:

19:53:27.461  CardCallback: btn_stop action received
19:53:27.566  [Finalize] Skipping — card stop was requested
19:53:29.013  [DingTalk][CardStop] stop succeeded
19:55:29.032  embedded run timeout: runId=aab122ed... timeoutMs=120000

The embedded run continued for exactly 120 seconds (the configured timeoutSeconds) after the stop was clicked. No abort was logged. No abortEmbeddedPiRun was called.

Root Cause

The DingTalk plugin's card callback handler (channel.ts) only:

  1. Sets stopped = true on the card draft controller
  2. Skips card finalization

It does not call chat.abort RPC or abortEmbeddedPiRun(sessionId).

The abort infrastructure exists in OpenClaw core:

  • chat.abort RPC method (src/gateway/server-methods/chat.ts)
  • abortEmbeddedPiRun(sessionId) (src/agents/pi-embedded-runner/runs.ts)
  • The /stop CLI command correctly calls abortEmbeddedPiRun()

Impact

  1. Wasted tokens: The run continues using tokens until timeout
  2. Confusing UX: New messages either get "✅ Done" (if run still busy) or are processed by the old run (if run ended around the same time)
  3. Inconsistent with /stop command: The CLI /stop command properly aborts the run, but the UI stop button does not

Environment

  • OpenClaw: v2026.4.2
  • DingTalk plugin: @soimy/dingtalk v3.5.2
  • Channel: DingTalk AI Card mode (cardRealTimeStream: true)
  • Timeout: 120000ms (default)

Suggested Fix

In the DingTalk plugin's card callback handler, when btn_stop is detected, also call chat.abort via the gateway RPC to properly abort the embedded run:

// In channel.ts card callback handler
if (analysis.actionId === 'btn_stop') {
  // Existing: stop the card streaming
  draft.stop()
  // Missing: abort the embedded run
  const chat = await this.#getChat(peer)
  await chat.abort()
}

Or call abortEmbeddedPiRun(sessionId) if accessible from the plugin context.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions