Skip to content

Bug: fitCodexProjectedContextForTurnStart returns text exceeding maxChars when header + user request fill the turn limit (overflows Codex turn/start) #94748

Description

@Nas01010101

Summary

fitCodexProjectedContextForTurnStart (extensions/codex/src/app-server/context-engine-projection.ts:121) can return prompt text longer than maxChars, breaking its own contract.

The doc-comment (:120) states it "Fits projected context prompts under Codex app-server turn/start text limits", and the committed test asserts fitted.length <= 420 (context-engine-projection.test.ts:221). The overflow path violates both. On main @ b306745f:

const beforeContext = params.promptText.slice(0, range.start);
const context = params.promptText.slice(range.start, range.end);
const afterContext = params.promptText.slice(range.end);
const contextBudget = maxChars - beforeContext.length - afterContext.length;
const fittedContext = truncateOlderContext(context, contextBudget); // returns "" when contextBudget <= 0
return `${beforeContext}${fittedContext}${afterContext}`;            // length still = before + after

When beforeContext.length + afterContext.length > maxChars, contextBudget is non-positive, truncateOlderContext returns "" (:447, the maxChars <= 0 guard), and the function returns beforeContext + "" + afterContext. The older context is silently dropped and the output still exceeds maxChars.

afterContext is \n</conversation_context>\n\nCurrent user request:\n${prompt} — the raw, unbounded user request — so this is reachable with ordinary large input, not a synthetic edge case.

Impact

Confirmed by running the actual code (verbatim copies of the three functions at b306745f):

Scenario maxChars before + after fitted.length <= maxChars?
header + large request, small cap 420 700 700
~2k context + ~1.05MB pasted request, default cap 1048576 1053628

Trigger: any turn where the context header plus the trailing user request already fill the limit (a large pasted request, or a small-model context budget where maxChars is low). The over-limit text is then handed to the Codex app-server, which hard-rejects the turn: MAX_USER_INPUT_TEXT_CHARS = 1 << 20 (openai/codex codex-rs/protocol/src/user_input.rs), enforced in the v2 turn/start + turn/steer path (codex-rs/app-server/src/request_processors/turn_processor.rs) with Input exceeds the maximum length of {N} characters. So instead of degrading gracefully, the projection emits input that Codex refuses — and below 1MB for small-model budgets the turn also carries less context than intended.

Proposed fix

In the contextBudget <= 0 branch, bound the remaining non-context text instead of returning it unbounded. Reuse the existing truncateOlderContext helper on beforeContext + afterContext: it keeps the tail, so the user's actual request survives while the older header text is truncated with the existing marker. Guarantees output.length <= maxChars in all cases; the happy path (contextBudget > 0) is unchanged. A branch is ready with regression tests for the small-cap and default-1<<20 cases (both reproduce the overflow before the fix).

Severity: Medium — needs a large prompt or a small-model maxChars budget to trigger; common in long-context / paste-heavy Codex turns, but not every turn.

Introduced by #94093 (fitCodexProjectedContextForTurnStart added 2026-06-18).

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:session-stateSession, memory, transcript, context, or agent state can drift or corrupt.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.no-staleExclude from stale automation

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions