Skip to content

Bug: preflight (budget-triggered) compaction hard-capped at ~60s, ignores compaction.timeoutSeconds #95553

Description

@kiagentkronos-cell

Bug: preflight (budget-triggered) compaction is hard-capped at ~60s by the reply-operation abort, ignores compaction.timeoutSeconds, and cannot be disabled or extended via config

Affected version: [email protected] (mechanism long-standing — preflight-timeout log lines observed back to 2026-05-22; confirmed still present in 2026.6.9)

Summary

OpenClaw runs two distinct compaction paths with two different effective timeouts:

Path Trigger Effective timeout Source
Overflow recovery trigger=overflow / timeout_recovery ~900s (15 min) resolveCompactionTimeoutMs(cfg) = agents.defaults.compaction.timeoutSeconds ?? 9e5
Preflight / budget trigger=budget / cli_budget ~60s replyOperation.abortSignal (NOT config-derived)

runPreflightCompactionIfNeeded (agent-runner.runtime) fires before a turn when totalTokens >= contextWindow - reserveTokensFloor - softThresholdTokens and calls compactEmbeddedAgentSession({ trigger: "budget", abortSignal: replyOperation.abortSignal }). Inside compactEmbeddedAgentSession the work races the configurable 15-min safety timeout against that abort signal — but for the preflight path the reply-operation abort wins at ~60s, so compaction.timeoutSeconds has no effect on this path.

On backends where summarizing a large context legitimately takes longer than 60s (big window + slower self-hosted model), preflight compaction can never complete: it times out at ~60s on every large turn, wastes those 60s, holds the session write lock for the duration, and then the turn proceeds un-compacted and falls through to overflow recovery anyway. Net effect: pure overhead before every large turn, plus added session-write-lock contention.

There is no config to (a) extend the preflight budget, or (b) disable preflight compaction. Compaction config keys are limited to enabled, mode, model, provider, reserveTokensFloor, softThresholdTokens, timeoutSeconds, maxHistoryShare, memoryFlush, maxActiveTranscriptBytes, midTurnPrecheck, keepRecentTokens. None gates preflight. compaction.enabled: false is the only global switch and it also disables overflow recovery, so it is not a viable workaround for a long-running agent. Setting memoryFlush.enabled: false does not disable preflight.

Impact

  • Every turn whose persisted context is above the preflight threshold eats a guaranteed ~60s of doomed preflight work before the model is even called.
  • The preflight compaction holds the session write lock during those 60s, contributing to SessionWriteLockTimeoutError / EmbeddedAttemptSessionTakeoverError on concurrent same-session traffic.
  • No knob to opt out or to give the path enough time to actually succeed.

Evidence

trigger=budget      outcome=failed reason=timeout durationMs≈60000   (cli_budget/budget: ~24 occurrences clustered at 60xxx ms)
trigger=overflow    outcome=failed reason=timeout durationMs≈900000
trigger=timeout_recovery outcome=failed reason=timeout durationMs≈900000

Paired compaction durations:

  • complete: 22, range 54–954s, median 278s (compactions routinely run minutes and succeed).
  • incomplete: 26, of which 24 at exactly 60–61s (the budget path), plus two outliers (375s, 1801s).

So a 60s bound is structurally impossible for this path: real compactions on this backend take ~278s median.

Environment

openclaw 2026.6.9
node v22.22.2
platform Linux aarch64 (NVIDIA GB10 / Spark)
provider openai-completions → vLLM, compaction model vllm/qwen3.6-fp8-fast
context window 196608, reserveTokensFloor 20000, softThresholdTokens 10000

Code references

  • agent-runner.runtime-*.jsrunPreflightCompactionIfNeeded (~L650), trigger: "budget", abortSignal: params.replyOperation.abortSignal; shouldRunPreflightCompaction / resolveMemoryFlushGateState
  • compact-*.jscompactWithSafetyTimeout(..., compactionTimeoutMs, { abortSignal: params.abortSignal }); [compaction-diag] end ... reason=${failureReason}
  • tool-result-middleware-*.jsresolveCompactionTimeoutMs, EMBEDDED_COMPACTION_TIMEOUT_MS = 9e5

Update (v2026.6.9)

Still present. Additional log evidence and a secondary failure mode confirmed by tracing the dist source.

Secondary failure chain: preflight abort → genuine overflow

When preflight compaction times out, the session remains near-full. On the next turn the context genuinely exceeds the window and triggers overflow-path compaction with willRetry=true — but that compaction also fails because it hits the preflight context too.

Log evidence (Jun 19–21 2026):

Jun 19 00:38  embedded run auto-compaction start: reason=overflow
Jun 19 00:39  embedded run auto-compaction incomplete: reason=overflow aborted=false willRetry=false
Jun 19 00:52  embedded run auto-compaction start: reason=overflow
Jun 19 00:53  embedded run auto-compaction incomplete: reason=overflow aborted=false willRetry=false
Jun 21 12:54  agents/cli-compaction: CLI transcript compaction failed for vllm/qwen3.6-fp8: Compaction timed out
Jun 21 12:54  [compaction-diag] trigger=cli_budget outcome=failed reason=timeout durationMs=183878

The overflow-incomplete events (willRetry=false) indicate the overflow recovery also bails without retrying — the 60s signal fires during the overflow path too, not just the budget path.

PR#91737 does not fix this

PR#91737 (merged 2026-06-10, not yet released as of 2026.6.9) fixes persisted totalTokens for cron session snapshots. It does not touch replyOperation.abortSignal, runPreflightCompactionIfNeeded, or the budget/cli_budget compaction path.

maxHistoryShare is not the trigger (clarification)

maxHistoryShare controls how much history is retained after compaction — it is evaluated only once compaction is already running. The compaction trigger does not read maxHistoryShare.

Proposed fix

Either:

  1. Honor agents.defaults.compaction.timeoutSeconds for the preflight/budget path too (dont let the reply-operation abort cap it below the configured compaction budget), or
  2. Add a config toggle agents.defaults.compaction.preflight: false to skip preflight compaction entirely (turns then rely on overflow recovery, which already has the 15-min budget).

Option 2 is the minimal, safe escape hatch for slow-backend deployments.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High-priority user-facing bug, regression, or broken workflow.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:needs-live-reproClawSweeper needs live local, crabbox, or manual validation to confirm this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:crash-loopCrash, hang, restart loop, or process-level availability failure.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🐚 platinum hermitGood issue quality with a plausible reproduction path needing some confirmation.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions