{"record":{"id":"7af61f548e22bcf3","repo":"can1357/oh-my-pi","slug":"snapcompact-cannot-run-locally-kept-history-alone","errorCode":null,"errorMessage":"snapcompact cannot run locally: kept history alone exceeds the context budget.","messagePattern":"snapcompact cannot run locally: kept history alone exceeds the context budget\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/session/session-maintenance.ts","lineNumber":914,"sourceCode":"\t\t\tlet codexCompaction: CodexCompactionContext | undefined;\n\n\t\t\t// Snapcompact runs locally first. The frame cap is sized from the live\n\t\t\t// model window via #computeSnapcompactMaxFrames so the post-render context\n\t\t\t// fits without the warning loop (issue #3247). A local blocker rejects\n\t\t\t// this method, allowing the configured preference order to continue.\n\t\t\tlet snapcompactResult: snapcompact.CompactionResult | undefined;\n\t\t\tif (snapcompactReady) {\n\t\t\t\tconst maxFrames = this.#computeSnapcompactMaxFrames(preparation, effectiveSettings);\n\t\t\t\tif (maxFrames < 1) {\n\t\t\t\t\tlogger.warn(\"Snapcompact skipped: kept history alone exceeds the context budget\", {\n\t\t\t\t\t\tmodel: this.#model?.id,\n\t\t\t\t\t});\n\t\t\t\t\tthis.#host.emitNotice(\n\t\t\t\t\t\t\"warning\",\n\t\t\t\t\t\t\"snapcompact: kept history alone exceeds the context budget.\",\n\t\t\t\t\t\t\"compaction\",\n\t\t\t\t\t);\n\t\t\t\t\tthrow new Error(\"snapcompact cannot run locally: kept history alone exceeds the context budget.\");\n\t\t\t\t} else {\n\t\t\t\t\tconst shape = snapcompactShape;\n\t\t\t\t\tif (!shape) {\n\t\t\t\t\t\tthrow new Error(\"snapcompact shape was not resolved before rendering.\");\n\t\t\t\t\t}\n\t\t\t\t\tsnapcompactResult = await snapcompact.compact(preparation, {\n\t\t\t\t\t\tconvertToLlm,\n\t\t\t\t\t\tmodel: this.#model,\n\t\t\t\t\t\t...(snapcompactShapeSetting === \"auto\" ? {} : { shape }),\n\t\t\t\t\t\tmaxFrames,\n\t\t\t\t\t\tincludeThinking: snapcompactIncludeThinking,\n\t\t\t\t\t});\n\t\t\t\t\tconst framePayloadBytes = this.#snapcompactFramePayloadBytes(snapcompactResult);\n\t\t\t\t\tif (framePayloadBytes > snapcompact.FRAME_DATA_BYTES_BUDGET) {\n\t\t\t\t\t\tlogger.warn(\"Snapcompact exceeded the per-request frame payload budget\", {\n\t\t\t\t\t\t\tmodel: this.#model?.id,\n\t\t\t\t\t\t\tframePayloadBytes,\n\t\t\t\t\t\t\tbudget: snapcompact.FRAME_DATA_BYTES_BUDGET,","sourceCodeStart":896,"sourceCodeEnd":932,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/session/session-maintenance.ts#L896-L932","documentation":"Thrown when snapcompact computes that even the kept (recent) history alone exceeds the model's context budget — after excluding what gets archived to images, the retained messages already fill (or overflow) the context window, so rendering the archive on top cannot fit. snapcompact aborts and emits a warning notice instead of producing a request the model would reject.","triggerScenarios":"Running snapcompact on a session where the recent kept messages are themselves enormous (e.g. one giant tool result or file dump in recent turns) against a small-context model; very low context-window model selected; snapcompact invoked far too late (context already blown).","commonSituations":"Pasting massive logs/files shortly before compacting; a model switch to a smaller-context model mid-session; automation that only compacts after the first API overflow.","solutions":["Run snapcompact earlier, before kept history grows past the budget.","Switch to a larger-context model for this compaction.","Remove or shrink oversized recent entries (e.g. huge tool outputs) from the session, or use a different compaction method that tolerates the load."],"exampleFix":"// before\nif (tokens > hardLimit) await session.compact({ mode: \"snapcompact\" }); // too late\n// after\nif (tokens > softLimit) await session.compact({ mode: \"snapcompact\" }); // compact earlier","handlingStrategy":"validation","validationCode":"const usage = session.getTokenUsage();\nconst ctx = session.getModel().contextWindow;\nif (usage.total > ctx * 0.6) {\n  // kept history may already exceed the snapcompact budget — compact earlier or switch model\n}","typeGuard":null,"tryCatchPattern":"try {\n  await session.compact({ mode: \"snapcompact\" });\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"exceeds the context budget\")) {\n    return session.compact({ mode: \"soft\" }); // tolerate heavier sessions\n  }\n  throw err;\n}","preventionTips":["Compact well before the context window fills; don't wait for API overflow.","Avoid dumping giant tool results/files in the turns right before compaction.","If using a small-context model, switch to a larger one before snapcompact."],"tags":["compaction","snapcompact","context-window","budget"],"backgroundTag":"context-window-exceeded","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}