fix(scheduler): back-fill deferred-output placeholders before overwrite (IndexError crash) - #1707
Draft
yhl-amd wants to merge 1 commit into
Draft
fix(scheduler): back-fill deferred-output placeholders before overwrite (IndexError crash)#1707yhl-amd wants to merge 1 commit into
yhl-amd wants to merge 1 commit into
Conversation
postprocess overwrites the last num_placeholder(+offset) slots of token_ids / output_tokens with the real sampled tokens, assuming the previous step's tail (the `if need_placeholder` loop) already appended those eos placeholder slots. That assumption breaks for a sequence whose prefill completes in a single chunk (e.g. a prefix-cache-heavy prompt that only forwards a few new tokens): it is never partial at step start, so it is not continue'd above, and it reaches the deferred-output write on its very first appearance with output_tokens still empty. The negative-index write then (a) raises `IndexError: list assignment index out of range` on output_tokens, killing EngineCore-DP0 — the API front-end lingers, so the container stays Up and /v1/models answers while requests pile up unprocessed; and (b) would otherwise silently overwrite the last PROMPT token in token_ids. Fix: back-fill the missing eos placeholder slots via append_token (keeping token_ids / output_tokens / num_tokens in lockstep) right before the overwrite loop. Self-healing, and a no-op on the normal path (the tail loop already reserved the slots, so the guard never fires). Repro: send a long prompt, then resend it (or a superset) so the second request hits the prefix cache and finishes prefill in one chunk. Observed on GLM-5.2 MXFP4, MTP off, tp8, chunked prefill on (chunk=16384), 1M max ctx. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
🏷️ CI GuideRuns automatically on every eligible PR before approval:
Heavy model tests:
|
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.
Symptom
EngineCore-DP0dies with:The uvicorn front-end survives, so the container stays Up and
/v1/modelsanswers, but requests just queue (nothing decodes). Reproduced twice on a
GLM-5.2 MXFP4 deploy (MTP off, tp8, chunked prefill on, chunk=16384, 1M ctx).
Root cause
The deferred-output write overwrites the last
num_placeholder (+offset)slotsof
token_ids/output_tokenswith the real sampled tokens, assuming theprevious step's tail (
if need_placeholder:loop) already appended thoseeos placeholder slots.
That holds for a normally chunk-prefilled seq: on the step its prefill
completes it is still partial at step start, gets
continue'd, receives itsplaceholder in the tail loop, and only writes on the next step.
It breaks for a seq whose prefill completes in a single chunk — e.g. a
prefix-cache-heavy prompt that only forwards a few new tokens (observed:
cached=33584, new=4). Such a seq is never partial at step start, so it isnot
continue'd, and reaches the deferred write on its first appearancewith
output_tokensstill empty. The negative index then:IndexErroronoutput_tokens→ engine core dies; andtoken_ids(
token_ids[-1]points at the prompt when no completion slot exists yet).Fix
Back-fill the missing eos placeholder slots via
append_token(keepingtoken_ids/output_tokens/num_tokensin lockstep) immediately before theoverwrite loop. Self-healing (any step arriving short fixes exactly what it
needs, regardless of whether the tail loop ran) and a no-op on the normal
path (slots already reserved → guard never fires).
Scope / risk
while len(output_tokens) < num_placeholder + offsetso the normal decode path is untouched.
single-chunk prefill completion.
Validation status
Root-caused from two captured crashes + code-path analysis (preempt and
abort_request were ruled out as red herrings — the aborted seq is
continue'dbefore the write). NOT yet run under a repro load. Reviewer note: repro plan
is to resend a long prompt so the second request hits the prefix cache and
completes prefill in one chunk, confirming crash on
mainand survival here,and verifying the first decoded token is correct (not the back-filled eos).
Draft pending that validation.
Supersedes #1703 (that PR chased a preempt() placeholder-strip hypothesis which
the second crash log refuted — no preempt occurred; the trigger is
single-chunk prefill completion, not preemption).
🤖 Generated with Claude Code