Skip to content

Commit 1f6ae32

Browse files
committed
fix(process): clamp queue task timeouts
1 parent 880425b commit 1f6ae32

2 files changed

Lines changed: 31 additions & 1 deletion

File tree

src/process/command-queue.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Command queue tests cover bounded command execution and queue ordering.
2+
import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion";
23
import { importFreshModule } from "openclaw/plugin-sdk/test-fixtures";
34
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
45
import { CommandLane } from "./lanes.js";
@@ -504,6 +505,34 @@ describe("command queue", () => {
504505
}
505506
});
506507

508+
it("clamps oversized task timeouts before arming lane timers", async () => {
509+
const lane = `timeout-clamp-lane-${Date.now()}-${Math.random().toString(16).slice(2)}`;
510+
setCommandLaneConcurrency(lane, 1);
511+
512+
vi.useFakeTimers();
513+
try {
514+
const setTimeoutSpy = vi.spyOn(globalThis, "setTimeout");
515+
const blocker = createDeferred();
516+
const task = enqueueCommandInLane(
517+
lane,
518+
async () => {
519+
await blocker.promise;
520+
},
521+
{ taskTimeoutMs: MAX_TIMER_TIMEOUT_MS + 1 },
522+
);
523+
524+
await Promise.resolve();
525+
526+
expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), MAX_TIMER_TIMEOUT_MS);
527+
528+
blocker.resolve();
529+
await task;
530+
setTimeoutSpy.mockRestore();
531+
} finally {
532+
vi.useRealTimers();
533+
}
534+
});
535+
507536
it("task timeout renews from progress timestamps", async () => {
508537
const lane = `timeout-progress-lane-${Date.now()}-${Math.random().toString(16).slice(2)}`;
509538
setCommandLaneConcurrency(lane, 1);

src/process/command-queue.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
logLaneDequeue,
55
logLaneEnqueue,
66
} from "../logging/diagnostic-runtime.js";
7+
import { clampPositiveTimerTimeoutMs } from "../shared/number-coercion.js";
78
import { resolveGlobalSingleton } from "../shared/global-singleton.js";
89
import type { CommandQueueEnqueueOptions } from "./command-queue.types.js";
910
import { CommandLane } from "./lanes.js";
@@ -244,7 +245,7 @@ function normalizeTaskTimeoutMs(value: number | undefined): number | undefined {
244245
if (value === undefined || !Number.isFinite(value) || value <= 0) {
245246
return undefined;
246247
}
247-
return Math.max(1, Math.floor(value));
248+
return clampPositiveTimerTimeoutMs(value);
248249
}
249250

250251
function resolveQueuePriority(priority: CommandQueueEnqueueOptions["priority"]): number {

0 commit comments

Comments
 (0)