Skip to content

fix(queue): survive a synchronous task throw in SerialQueue - #892

Open
YOMXXX wants to merge 2 commits into
TencentCloud:feat/server_teamfrom
YOMXXX:fix/518-serial-queue-sync-throw
Open

fix(queue): survive a synchronous task throw in SerialQueue#892
YOMXXX wants to merge 2 commits into
TencentCloud:feat/server_teamfrom
YOMXXX:fix/518-serial-queue-sync-throw

Conversation

@YOMXXX

@YOMXXX YOMXXX commented Aug 9, 2026

Copy link
Copy Markdown

Fixes #518.

Problem

SerialQueue.drain() called entry.task() directly. If a queued task throws synchronously, the call escapes before the .finally() handler is registered — the promise returned by add() rejects, but the queue stays permanently stuck with running = true.

This affects all three pipeline queues (L1/L2/L3, pipeline-manager.ts). Subsequent tasks never start, and callers such as flushSession() and _doFlush() can wait forever on onIdle().

Reproduction (on main):

const queue = new SerialQueue("repro");
await queue.add(() => { throw new Error("sync failure"); }).catch(() => undefined);
const next = queue.add(async () => "ran");
// hangs forever — queue.running stays true

Change

Invoke the task from a resolved promise (Promise.resolve().then(() => entry.task())) so a synchronous throw is routed through the catch/finally chain and queue bookkeeping is finalized.

Verification

  • New src/utils/serial-queue.test.ts (4 tests): sync-throw recovery, FIFO order across mixed tasks, onIdle() after a sync-throw, and no double-run.
  • Confirmed the first test hangs on the unfixed code and passes after the fix.
  • npm test + npm run build:plugin pass.

YOMXXX added 2 commits August 9, 2026 18:13
Issue TencentCloud#851: the plugin used to monkey-patch OpenClaw's compiled dist files
to inject event.messages into after_tool_call. The patch is removed in v2,
but the hook still gated L3 compression on classifyPatchEffectiveness() and
relied on the injected messages — on current OpenClaw versions (where
event.messages is absent) L3/MMD never ran.

- after_tool_call now fetches session messages through the official
  api.runtime.subagent.getSessionMessages({ sessionKey, limit }) when the
  event carries none
- removed the classifyPatchEffectiveness() patch check (the patch no longer
  exists)
- the handler takes an optional getSessionMessages callback, wired from the
  plugin api at registration

Fixes TencentCloud#851 (replaces the fragile dist-patch approach with the supported API)

Signed-off-by: 李冠辰 <liguanchen@xiaomi.com>
drain() invoked entry.task() directly; when a queued task threw
synchronously the call escaped before .finally() was registered,
leaving running=true forever. The queue stayed stuck, so every later
add() hung and onIdle() never resolved — silently stalling L1/L2/L3
flushes in the pipeline.

Wrap the task call in Promise.resolve().then() so the error is caught,
bookkeeping is finalized, and the next queued task runs normally.

Fixes TencentCloud#518

Signed-off-by: 李冠辰 <liguanchen@xiaomi.com>
@Maxwell-Code07

Copy link
Copy Markdown
Collaborator

Thanks for your contribution and attention! We will review this PR and get back to you as soon as possible.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: SerialQueue deadlocks after a synchronous task throw

2 participants