Repository navigation
Auto-compaction triggers at ~200k tokens instead of model's actual context window (1M) #15871
Description
Summary
When using context1m: true with Claude Opus 4.6 or Sonnet 4.6, the Memory Flush hook computes its threshold from a hardcoded contextWindow: 200000 in models.generated.js instead of the actual 1M context window. This results in compaction at ~144k tokens (200k − 50k reserve − 6k soft threshold).
Environment
- OpenClaw 2026.3.2 (9288d93)
- Model:
anthropic/claude-opus-4-6withcontext1m: true - Compaction mode:
safeguard reserveTokensFloor: 50000,softThresholdTokens: 6000
Reproduction
- Configure
anthropic/claude-opus-4-6withparams.context1m: true - Use the session until context reaches ~144k tokens
- Memory Flush triggers compaction
Expected: No compaction below ~944k (1M − 50k reserve − 6k soft)
Actual: Compaction at ~144k (200k − 50k − 6k)
Evidence
6 consecutive compactions, all fromHook: True (Memory Flush):
| Timestamp (UTC) | tokensBefore |
|---|---|
| 2026-02-28 03:41 | 157,265 |
| 2026-03-01 22:54 | 271,529 |
| 2026-03-03 05:28 | 264,061 |
| 2026-03-03 15:33 | 249,187 |
| 2026-03-03 16:09 | 163,063 |
| 2026-03-03 20:32 | 150,573 |
Root Cause
1. models.generated.js hardcodes contextWindow: 200000
File: /app/node_modules/.pnpm/@mariozechner+pi-ai@0.55.3_ws@8.19.0_zod@4.3.6/node_modules/@mariozechner/pi-ai/dist/models.generated.js
Both claude-opus-4-6 and claude-sonnet-4-6 have contextWindow: 200000. This value is cached in MODEL_CACHE at startup via applyDiscoveredContextWindows().
2. resolveMemoryFlushContextWindowTokens ignores context1m
// pi-embedded-DgYXShcG.js, line 47871
function resolveMemoryFlushContextWindowTokens(params) {
return lookupContextTokens(params.modelId) ?? params.agentCfgContextTokens ?? DEFAULT_CONTEXT_TOKENS;
}This calls lookupContextTokens() which returns the cached 200k. It does not call resolveContextTokensForModel() (line 22146), which correctly checks context1m:
function resolveContextTokensForModel(params) {
// ...
if (resolveConfiguredModelParams(params.cfg, ref.provider, ref.model)?.context1m === true
&& isAnthropic1MModel$1(ref.provider, ref.model))
return ANTHROPIC_CONTEXT_1M_TOKENS; // 1,048,576
// ...
}3. applyDiscoveredContextWindows takes minimum for duplicates
// pi-embedded-DgYXShcG.js, line 22021
if (existing === void 0 || contextWindow < existing) params.cache.set(model.id, contextWindow);If a model appears with both 200k and 1M entries, 200k wins.
4. lookupContextTokens has a race condition
// pi-embedded-DgYXShcG.js, line 22107
function lookupContextTokens(modelId) {
ensureContextWindowCacheLoaded(); // async — NOT awaited
return MODEL_CACHE.get(modelId); // may return undefined if cache not loaded
}On startup, the cache may be empty → undefined → falls back to DEFAULT_CONTEXT_TOKENS (200k).
5. DEFAULT_CONTEXT_TOKENS = 2e5
// model-selection-Zb7eBzSY.js, line 30 (6 copies across dist/)
const DEFAULT_CONTEXT_TOKENS = 2e5;The last-resort fallback is also 200k.
6. Heartbeat model bypasses context-1m header
// pi-embedded-DgYXShcG.js, line 88969
if (extraParams?.context1m === true)
if (isAnthropic1MModel(modelId)) betas.add(ANTHROPIC_CONTEXT_1M_BETA);isAnthropic1MModel() matches only claude-opus-4 and claude-sonnet-4 prefixes. If the heartbeat uses a different model (e.g. Haiku), the context-1m header is not sent — but the full session history (built at 1M) is. Anthropic enforces 200k → overflow compaction.
7. Compaction uses triggering model, not session model
// pi-embedded-DgYXShcG.js, line 94460
model: modelId // uses the model that triggered the overflowThe compaction API call uses the triggering model (e.g. Haiku). Since Haiku is not a 1M model, the compaction request also lacks the context-1m header — so Anthropic enforces 200k on the compaction call itself. Additionally, a weaker model writes the summary instead of the session's primary model.
Suggested Fixes
resolveMemoryFlushContextWindowTokensshould useresolveContextTokensForModel— this already correctly handlescontext1mand returns 1M- Update
models.generated.js—claude-opus-4-6andclaude-sonnet-4-6should havecontextWindow: 1048576 applyDiscoveredContextWindowsshould take maximum, not minimum, for duplicate entrieslookupContextTokensshould awaitensureContextWindowCacheLoaded()or ensure synchronous populationDEFAULT_CONTEXT_TOKENSshould be 1M or derived from the configured model- Heartbeat should use the session's
context-1mheader if the session was built with a 1M model - Compaction should use the session's primary model, not the triggering model
Workaround
1. Patch models.generated.js
find /path/to/openclaw -name "models.generated.js" \
-exec sed -i 's/contextWindow: 200000/contextWindow: 1048576/g' {} +2. Patch DEFAULT_CONTEXT_TOKENS (defense-in-depth for race condition)
find /path/to/openclaw/dist -name "*.js" \
-exec sed -i 's/const DEFAULT_CONTEXT_TOKENS = 2e5/const DEFAULT_CONTEXT_TOKENS = 1048576/g' {} +Both patches must be reapplied after every update. Restart OpenClaw after patching.
Files Affected
| File | Line | Issue |
|---|---|---|
models.generated.js |
various | Hardcoded contextWindow: 200000 |
pi-embedded-DgYXShcG.js |
47871 | Memory flush ignores context1m |
pi-embedded-DgYXShcG.js |
22021 | Min-value for duplicate entries |
pi-embedded-DgYXShcG.js |
22107 | Race condition (async not awaited) |
model-selection-Zb7eBzSY.js |
30 | DEFAULT_CONTEXT_TOKENS = 2e5 |
pi-embedded-DgYXShcG.js |
88969 | Heartbeat bypasses context-1m header |
pi-embedded-DgYXShcG.js |
94460 | Compaction uses triggering model |
Activity
github-actions commentedon Mar 3, 2026
This issue might be a duplicate of existing issues. Please check:
context-1m-2025-08-07not sent for Claude Opus 4.6, causing 200k hard limit #12507: Same root cause —context-1mbeta header not being sent for Claude Opus 4.6, resulting in the 200k token hard limit being enforced instead of the model's actual 1M context window. Issue 1 in your root cause analysis appears to already be tracked there.If your report covers additional distinct bugs not addressed in those issues (Issues 2–6 in your analysis), it may be worth linking to them and focusing this issue on the remaining gaps.
DusKing1 commentedon Mar 7, 2026
The premature compaction at ~200k is caused by two things: (1) missing
context-1m-2025-08-07beta header, and (2) default model limits in OpenCode being set to 200k.For Anthropic API users, I made a plugin that fixes both. Add to your
opencode.json:{ "plugin": ["opencode-anthropic-context-1m"], "provider": { "anthropic": { "models": { "claude-opus-4-6": { "limit": { "context": 1000000, "output": 128000 } }, "claude-sonnet-4-6": { "limit": { "context": 1000000, "output": 64000 } }, "claude-sonnet-4-5": { "limit": { "context": 1000000, "output": 64000 } }, "claude-sonnet-4-20250514": { "limit": { "context": 1000000, "output": 64000 } } } } } }The plugin injects the beta header via
chat.headershook (appends, doesn't overwrite existing SDK flags). The model overrides push compaction threshold from ~168K to ~968K.https://github.com/DusKing1/opencode-anthropic-context-1m
fix: resolve 8 open bug reports from upstream tracker
jason2077 commentedon Mar 15, 2026
I can add one more high-confidence data point from a real
opencode v1.2.26session.Evidence (DB)
ses_3...max_total = 382,889>=200k:2026-03-15 13:07:07>=300k:2026-03-15 15:40:32part.type='compaction' = 0message.mode='compaction' = 0Runtime sanity
OPENCODE_DISABLE_AUTOCOMPACTwas unsetOPENCODE_DISABLE_PRUNEwas unsetcompaction.auto === false(explicit false check)Overflow-threshold math used by core
In
session/compaction.ts, overflow uses:usable = input ? input - reserved : context - maxOutputTokens(model)maxOutputTokens = min(model.limit.output, 32000)(fromprovider/transform.ts)Local
~/.cache/opencode/models.jsonin this environment reports:anthropic/claude-opus-4-6:context=1,000,000,output=128,000anthropic/claude-sonnet-4-6:context=1,000,000,output=64,000So derived overflow threshold for both is:
usable = 1,000,000 - 32,000 = 968,000Observed implication
Given those values, a session at
382,889is still below the overflow threshold, which is consistent with observing zero compaction markers.Next-session checklist (I plan to run and report back)
imqqmi commentedon Mar 15, 2026
Would also be nice if we can just set this manually per model in the opencode.json file, ie set opus 4.6 to 200K instead of 1M because token usage shoots up beyond that.
logos007 commentedon Mar 21, 2026
The issue is still existing in v2026.3.13.
rekram1-node commentedon May 13, 2026
this issue seems to cite pi mono not opencode or some other tool