{"record":{"id":"e8fb714820b2c36c","repo":"sipeed/picoclaw","slug":"context-window-still-exceeded-after-retry-compacti","errorCode":null,"errorMessage":"context window still exceeded after retry compaction; refusing to drop active turn messages: %w","messagePattern":"context window still exceeded after retry compaction; refusing to drop active turn messages: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/agent/pipeline_llm.go","lineNumber":443,"sourceCode":"\t\t\t\t\t\"retry\":           retry,\n\t\t\t\t\t\"dropped_msgs\":    dropped,\n\t\t\t\t\t\"remaining_msgs\":  len(exec.history),\n\t\t\t\t\t\"context_window\":  ts.agent.ContextWindow,\n\t\t\t\t\t\"max_tokens\":      ts.agent.MaxTokens,\n\t\t\t\t\t\"still_overlimit\": !fit,\n\t\t\t\t})\n\t\t\t} else if !fit {\n\t\t\t\tlogger.WarnCF(\"agent\", \"Context still exceeds budget after retry compaction rebuild\", map[string]any{\n\t\t\t\t\t\"session_key\":         ts.sessionKey,\n\t\t\t\t\t\"retry\":               retry,\n\t\t\t\t\t\"history_msgs\":        len(exec.history),\n\t\t\t\t\t\"protected_turn_msgs\": len(protectedTurnTail),\n\t\t\t\t\t\"context_window\":      ts.agent.ContextWindow,\n\t\t\t\t\t\"max_tokens\":          ts.agent.MaxTokens,\n\t\t\t\t})\n\t\t\t}\n\t\t\tif !fit {\n\t\t\t\terr = fmt.Errorf(\n\t\t\t\t\t\"context window still exceeded after retry compaction; refusing to drop active turn messages: %w\",\n\t\t\t\t\terr,\n\t\t\t\t)\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tbreak\n\t}\n\n\tif err != nil {\n\t\tal.emitEvent(\n\t\t\truntimeevents.KindAgentError,\n\t\t\tts.eventMeta(\"runTurn\", \"turn.error\"),\n\t\t\tErrorPayload{\n\t\t\t\tStage:   \"llm\",\n\t\t\t\tMessage: err.Error(),\n\t\t\t},","sourceCodeStart":425,"sourceCodeEnd":461,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/agent/pipeline_llm.go#L425-L461","documentation":"Thrown in the LLM retry loop (pipeline_llm.go) after a context-compaction rebuild was attempted and the message history STILL does not fit the context budget. The library deliberately refuses to shrink further because that would require dropping messages from the active turn (protectedTurnTail), which would corrupt in-flight conversation state; it wraps the original fit error instead.","triggerScenarios":"agent.ContextWindow/MaxTokens too small for: protected active-turn messages (large tool outputs, huge pasted files) + compacted history. Compaction already trimmed what it could (logged via 'Context still exceeds budget after retry compaction rebuild'), the fit check fails again, and the loop aborts with this error.","commonSituations":"Huge tool results (file reads, command dumps) inside the current turn; a small-window model (7k-8k) assigned to an agent with verbose tools; long sessions whose compacted summary plus turn tail still exceed the window; MaxTokens set close to ContextWindow leaving no room for history.","solutions":["Increase the agent's ContextWindow (or move to a larger-context model) so the active turn + compacted history fits","Reduce the size of current-turn tool outputs: truncate/limit tool results before they enter history","Start a new session or clear history so the compacted baseline is smaller","Verify MaxTokens leaves headroom — it consumes part of the window budget"],"exampleFix":"# before\nagents:\n  defaults:\n    model: llama-3-8k-text   # tiny window, huge tool outputs\n\n# after\nagents:\n  defaults:\n    model: gpt-4o            # 128k window\n    max_tokens: 4096","handlingStrategy":"validation","validationCode":"func estimateTokens(msgs []providers.Message) int {\n    n := 0\n    for _, m := range msgs {\n        n += len(m.Content) / 4 // rough chars-per-token heuristic\n    }\n    return n\n}\n\n// before the turn: refuse to send if the active turn alone cannot fit\nif estimateTokens(exec.messages) > ts.agent.ContextWindow-ts.agent.MaxTokens {\n    return fmt.Errorf(\"history too large for window %d; trim tool outputs or widen window\", ts.agent.ContextWindow)\n}","typeGuard":"func isContextOverflowError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"context window still exceeded\")\n}","tryCatchPattern":"if err := runTurn(); isContextOverflowError(err) {\n    // recover by starting a fresh session or widening the window, then retry\n    session.Reset()\n    widenContextWindow(agent)\n    return runTurn()\n}","preventionTips":["Truncate large tool outputs before they enter history","Choose models with context windows comfortably above worst-case turn size","Keep MaxTokens well below ContextWindow to leave room for history"],"tags":["context-window","compaction","llm","config","go"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}