fix(overflow): arm self-heal on ambiguous no-body 4xx errors - #215
Open
ranxianglei wants to merge 1 commit into
Open
fix(overflow): arm self-heal on ambiguous no-body 4xx errors#215ranxianglei wants to merge 1 commit into
ranxianglei wants to merge 1 commit into
Conversation
pi surfaces a bodyless provider 4xx verbatim as '400/413 status code (no body)' — pi-ai's own classifier treats it as overflow (anchored regex /^4(?:00|13)\s*(?:status code)?\s*\(no body\)/i), but OVERFLOW_MARKER never matched it, so the extension-side self-heal never armed. Incident 2026-08-23: a 50,358-char bash toolResult (~31.5k tokens of a 131,072 effective window) pushed every request past sglang's input+max_tokens cap; each retry returned the no-body 400 forever, the model never got a successful turn to compress, and user 'continue' just resent the same oversized context (dead loop). Treat the no-body text as a POSSIBLE overflow, armed only with corroboration (the same text serves non-overflow 4xx — invalid model, malformed request; see messages.ts): - sent-view estimate >= 50% of the effective limit (same pct basis as the turn log), OR - >= 2nd consecutive no-body 4xx since the last successful assistant turn (count resets on success/session start; runtime in-memory like the armed flag — not persisted). A bodyless error parses no window, so none is learned: the armed emergency uses the already-resolved effective limit. The classic text-marker arm path is unchanged. Relates #204; complements acp-kernel #133.
📦 Built Extension ArtifactBranch: Option A — Install from npm PR tag (recommended)pi install npm:billion-context-pi@pr-215Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf billion-context-pi-pr215.tgz
pi install ./packageThis comment is automatically updated on each push. |
Owner
Author
|
Review round 1 (agent reviewer — verdict: ship):
Minor:
|
This was referenced Aug 23, 2026
Merged
This was referenced Sep 5, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(overflow): arm self-heal on ambiguous no-body 4xx errors
Problem
Real incident (2026-08-23): an ACP-managed pi session dead-looped. A 50,358-char bash
toolResult(~31,475 tokens, ~24% of the effective 131,072-token window) pushed the request past sglang'sinput + max_tokens ≤ 262,144hard cap. Every subsequent request returned400 status code (no body)forever:compress— the ACP loop never shrank the context.OVERFLOW_MARKERtext markers (maximum context length is N,prompt is too long, …), and a bodyless 4xx carries none of them.OVERFLOW_PATTERNSends with/^4(?:00|13)\s*(?:status code)?\s*\(no body\)/i), but the extension-side marker set deliberately never matched it — for good reason: OpenAI-compatible providers return the exact same no-body 400 for non-overflow reasons (invalid model, malformed request; seesrc/messages.ts:80-82), so treating it as an unconditional overflow risks false positives.Fix
Treat pi's bodyless
4xx ... (no body)error text as a possible overflow signal, and arm the emergency self-heal (forced ≥95% usage → kernel emergency nudge + tool-result truncate) only when at least one false-positive guard holds:NO_BODY_ARM_RATIO) of the effective limit — the same pct basis already logged per turn (tokenCount / config.modelContextLimit, recorded per request in thecontextevent, before the armed boost so the guard sees the real estimate).OverflowEpisode), recovering the dead-loop even when the estimate under-reports (the incident ran at ~24%: the sent-view estimate cannot see the provider'sinput+max_tokensaccounting).When the body has no parseable limit number (always, for a bodyless error), no window is learned — the armed emergency uses the already-resolved effective limit, so arming never fails for lack of a parsed number.
The classic text-marker arm path (
inspectOverflowMessage→ learn window + arm) is unchanged.Files changed
src/overflow-selfheal.tsNO_BODY_4XX_MARKER+isNoBody4xxError()(mirrors pi-ai's anchored regex);NO_BODY_ARM_RATIO = 0.5;OverflowEpisode.noteSentView/noteSuccess/onNoBody4xx()(returns{arm, consecutive, ratio});reset()clears the new state. Guard state is runtime/in-memory like thearmedflag — not persisted to acp.json (justified in comment: a resumed session re-establishes both within one turn; a persisted count would arm against a fresh session whose first request may succeed).src/index.tswireContextTransform: recordnoteSentView(tokenCount, config.modelContextLimit)before the armed boost.wireOverflowSelfHeal(message_end): non-error assistant turns callnoteSuccess(); error turns run the classic marker path unchanged, else the no-body path with the two guards; arms + logsno-body-arm(consecutive, ratio) + UI notify.tests/overflow-selfheal.test.tsCHANGELOG.mdTests
npm run typecheck✅ · full suitenpm test✅ 422 pass / 0 fail (was 412; +10):isNoBody4xxErrormatches400/413 status code (no body),400 (no body),413(no body); rejects429/500/404 (no body), quota text, empty, and prefixed text (anchored, like pi-ai).>=case).maximum context length is 262144 ...→ still arms via the text-marker path, window parsed (regression).OverflowEpisode.reset()clears sent-view + count; fresh episode (no recorded sent view) does not arm on the first hit.Notes
isThrottleError) is unaffected (a no-body 4xx never matched its throttle phrases).