sgl-project/sglang · error · Error

delta payload size mismatch: expected ${expectedSize}, got $

Error message

delta payload size mismatch: expected ${expectedSize}, got ${restored.length}

What it means

Thrown by ngram_corpus Param::parse when two configured ranges overlap: the parser marks each position it fills, and filling an already-marked position means the config assigns a value to the same token position twice. Ranges must be disjoint.

Source

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

  }
  return items;
}

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) {

View on GitHub (pinned to 0132848349)

Solutions

  1. Make all ranges in the config disjoint, e.g. '2-4|8 5-8|3' instead of '2-4|8 3-5|3'
  2. Remember ranges are inclusive on both ends — adjacent ranges must not share an endpoint
  3. Write a quick Python check that merges/validates interval overlap before passing the string to C++

Example fix

# before
param.resetBatchReturnTokenNum("2-4|8 3-5|3")

# after
param.resetBatchReturnTokenNum("2-4|8 5-8|3")
Defensive patterns

Strategy: validation

Validate before calling

ivs = sorted(tuple(map(int, s.split("|")[0].split("-"))) for s in cfg.split())
for (a1, b1), (a2, b2) in zip(ivs, ivs[1:]):
    assert b1 < a2, f"overlapping ranges {a1}-{b1} and {a2}-{b2}"

Type guard

def ranges_disjoint(cfg: str) -> bool:
    ivs = sorted(tuple(map(int, s.split("|")[0].split("-"))) for s in cfg.split())
    return all(b1 < a2 for (_, b1), (a2, _) in zip(ivs, ivs[1:]))

Try / catch

try:
    param.resetBatchReturnTokenNum(cfg)
except RuntimeError as e:
    if "repeated position" in str(e):
        raise ValueError(f"overlapping ranges in {cfg!r}; make them disjoint") from e
    raise

Prevention

When it happens

Trigger: resetBatchReturnTokenNum with overlapping segments such as '2-4|8 3-5|3' — position 3 and 4 are covered by both ranges, so mark[L] is already true on the second pass.

Common situations: Appending a new range to an existing config without checking for overlap, or ranges like '2-4|8 4-6|3' that share an endpoint (inclusive ranges).

Related errors


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