sgl-project/sglang · error · Error

Missing previous frame for delta payload

Error message

Missing previous frame for delta payload

What it means

SuffixAutomaton::appendTokens refuses to add tokens once finalize() has been called (finalized_ flag). A finalized SAM is a read-only query structure for longest-match lookups, so appending would invalidate its internal indexes.

Source

Thrown at python/sglang/multimodal_gen/apps/realtime_webui/app.js:1079

async function gunzipBytes(payload) {
  if (typeof DecompressionStream === "undefined") {
    throw new Error("This browser does not support gzip stream decoding");
  }
  const stream = new Blob([payload]).stream().pipeThrough(new DecompressionStream("gzip"));
  return new Uint8Array(await new Response(stream).arrayBuffer());
}

async function restoreDeltaGzipRawRgb(header, payload) {
  const frameBytes = Number(header.bytes_per_frame);
  const count = Number(header.num_frames);
  const expectedSize = frameBytes * count;
  const restored = await gunzipBytes(payload);
  if (restored.length !== expectedSize) {
    throw new Error(`delta payload size mismatch: expected ${expectedSize}, got ${restored.length}`);
  }
  let previous = header.delta_reference === "previous-frame" ? lastRawRgbFrame : null;
  if (header.delta_reference === "previous-frame" && !previous) {
    throw new Error("Missing previous frame for delta payload");
  }
  for (let f = 0; f < count; f++) {
    const current = f * frameBytes;
    if (previous) {
      for (let i = 0; i < frameBytes; i++) {
        restored[current + i] ^= previous[i];
      }
    }
    previous = restored.slice(current, current + frameBytes);
  }
  return restored;
}

async function framePayloadToImageData(header, payload) {
  let rawPayload;
  const isRgba = header.content_type === RAW_RGBA_DELTA_GZIP_CONTENT_TYPE;
  if (
    header.content_type === WEBP_FRAME_CONTENT_TYPE ||

View on GitHub (pinned to 0132848349)

Solutions

  1. Call reset()/re-create the SAM before appending new tokens after a finalize
  2. Restructure so all appendTokens calls happen before the single finalize() call
  3. Audit early-finalize paths (exceptions, early returns) if you believe the SAM shouldn't be finalized yet

Example fix

// before
sam.appendTokens(prompt1); sam.finalize();
sam.appendTokens(prompt2); // throws

// after
sam.appendTokens(prompt1); sam.finalize();
// ... matching on prompt1 ...
sam.reset();
sam.appendTokens(prompt2); sam.finalize();
Defensive patterns

Strategy: validation

Validate before calling

if sam.finalized:  # or track finalize() calls yourself
    sam.reset()
sam.appendTokens(tokens)

Type guard

def can_append(sam) -> bool:
    return not getattr(sam, "finalized_", False) and not getattr(sam, "is_finalized", lambda: False)()

Try / catch

try:
    sam.appendTokens(tokens)
except RuntimeError as e:
    if "after finalizing" in str(e):
        sam.reset(); sam.appendTokens(tokens)
    else:
        raise

Prevention

When it happens

Trigger: Calling appendTokens() on a SuffixAutomaton after finalize() was invoked — e.g. trying to stream more prompt tokens into a SAM already finalized for matching, or reusing a SAM object across requests without reset.

Common situations: Reusing a per-request suffix automaton across generations without resetting, incremental corpus updates after the matcher was built, or control-flow bugs where finalize() runs early (e.g. in an error path) before the final appendTokens.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/589dcafc302475a3. Report an issue: GitHub.