Skip to content

[Bug]: /compact command cannot be canceled while in progress #66535

Description

@xiaohuaxi

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

/compact invokes runtime.compactEmbeddedPiSession(...) without passing any abortSignal and without registering the compaction session into ACTIVE_EMBEDDED_RUNS. As a result neither chat.abort, abortEmbeddedPiRun(sessionId), nor /stop has any effect on an in-progress /compact; the user has no way to cancel until compactionTimeoutMs expires.

Steps to reproduce

  1. Start OpenClaw on a session that has accumulated enough messages for /compact to take tens of seconds or longer.
  2. In the chat, send /compact.
  3. While compaction is running, attempt to cancel via any available path: chat.abort, abortEmbeddedPiRun(sessionId) (directly or via the globalThis[Symbol.for("openclaw.embeddedRunState")].activeRuns.get(sessionId).abort() side door), or /stop.
  4. Observe that the compaction continues regardless.

Expected behavior

Invoking any supported cancel path should interrupt the compaction within a few seconds, consistent with any other embedded agent run. The compaction session should also appear in ACTIVE_EMBEDDED_RUNS for the duration, so external observers can discover it.

Actual behavior

All cancel attempts are no-ops. abortEmbeddedPiRun(sessionId) returns false immediately because the compaction session was never registered. chat.abort similarly finds nothing in chatAbortControllers. The compaction only ends when it completes successfully or when compactionTimeoutMs fires.

OpenClaw version

2026.4.12

Operating system

Ubuntu 22.04.5 LTS on WSL2 (Linux 6.6.87.2-microsoft-standard-WSL2)

Install method

npm global

Model

N/A (compaction uses whatever model the session is configured with; the cancel bug is independent of model selection)

Provider / routing chain

N/A (cancel path is orthogonal to provider routing)

Additional provider/model setup details

N/A

Logs, screenshots, and evidence

src/auto-reply/reply/commands-compact.ts (the current /compact handler, approximately L72-145): the handler first invokes abortEmbeddedPiRun(sessionId) + waitForEmbeddedPiRunEnd(sessionId, 15_000) to stop the current run, then calls runtime.compactEmbeddedPiSession({...}) without an abortSignal argument and without calling setActiveEmbeddedRun for the compaction itself.

The type definition in src/agents/pi-embedded-runner/compact.types.ts:56 already declares:

abortSignal?: AbortSignal;

on CompactEmbeddedPiSessionParams, so the parameter surface is prepared; the command path is simply not wiring it up. src/agents/pi-embedded-runner/compact.ts:520 (prepareCompactionSessionAgent) already passes an internal runAbortController.signal to a sub-session, so the inner mechanics exist.

Impact and severity

  • Affected: any user who invokes /compact on a long-running or complex session.
  • Severity: Medium. No data loss, no crash, but the user is locked out of progress until the compaction or its timeout completes. On slow providers or large sessions this can be several minutes.
  • Frequency: Every /compact that the user wants to abandon — for example when they realize they sent the command prematurely, or when compaction is visibly stuck.
  • Consequence: User frustration, wasted tokens on an unwanted compaction, and in at least one downstream UX (CoClaw UI) an explicit "disable cancel button during /compact" workaround to prevent UI state from diverging from the unkillable server-side operation.

Additional information

Minimum fix: in commands-compact.ts, construct an AbortController, pass its signal via the already-defined abortSignal field on CompactEmbeddedPiSessionParams, and register the compaction into ACTIVE_EMBEDDED_RUNS via setActiveEmbeddedRun(sessionId, handle, sessionKey) so that existing cancel primitives (chat.abort, abortEmbeddedPiRun) find it. The bulk of the type and inner-session plumbing is already in place.

Downstream workaround: UI clients can disable the cancel button while a /compact is in flight, so the user is not misled into expecting cancel to work. The CoClaw UI currently does this via a cancelDisabled prop on its chat input component.

Verified against OpenClaw commit d7cc6f7643 (v2026.4.14-beta.1+69).


Reported by the CoClaw team.
This issue was discovered while developing @coclaw/openclaw-coclaw, a CoClaw channel plugin for OpenClaw.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions