{"record":{"id":"7431dfff57864220","repo":"sgl-project/sglang","slug":"max-new-tokens-must-be-non-negative-got-max-new","errorCode":null,"errorMessage":"max_new_tokens must be non-negative, got {max_new_tokens}","messagePattern":"max_new_tokens must be non-negative, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/multimodal/processors/dots_note_omni.py","lineNumber":51,"sourceCode":"logger = logging.getLogger(__name__)\n\n_VIDEO_TOKEN_RE = re.compile(r\"(<image_\\d+>|<audio_\\d+>)\")\n_EXPANDED_VIDEO_MEDIA_RE = re.compile(\n    r\"<\\|sglang_dots_video_(?P<video>\\d+)_(?P<modality>image|audio)_(?P<item>\\d+)\\|>\"\n)\n\n\ndef _build_video_cfg(\n    *,\n    seq: int,\n    audio_cap: float,\n    audio_sr: int,\n    max_new_tokens: int,\n) -> dict[str, Any]:\n    if seq <= 0:\n        raise ValueError(f\"seq must be positive, got {seq}\")\n    if max_new_tokens < 0:\n        raise ValueError(f\"max_new_tokens must be non-negative, got {max_new_tokens}\")\n    if max_new_tokens >= seq:\n        raise ValueError(\n            \"max_new_tokens must leave room for input: \"\n            f\"max_new_tokens={max_new_tokens}, seq={seq}\"\n        )\n    if audio_cap < 0:\n        raise ValueError(f\"audio_cap must be non-negative, got {audio_cap}\")\n    if audio_sr <= 0:\n        raise ValueError(f\"audio_sr must be positive, got {audio_sr}\")\n\n    return {\n        \"process_audio\": audio_cap > 0,\n        \"seq_length\": seq - max_new_tokens,\n        \"reserve_interleave\": True,\n        \"audio_token_ratio_cap\": float(audio_cap),\n        \"audio_sample_rate\": int(audio_sr),\n        \"video_jpeg_quality\": int(os.environ.get(\"XHS_VIDEO_JPEG_QUALITY\", \"85\")),\n    }","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/multimodal/processors/dots_note_omni.py#L33-L69","documentation":"Raised by _build_video_cfg when max_new_tokens is negative. max_new_tokens is carved out of the seq budget for generation; a negative value is treated as a caller bug and rejected before the config dict is built.","triggerScenarios":"Passing a negative max_new_tokens via sampling_params in a video request, e.g. sampling_params={\"max_new_tokens\": -5}. Note the processor reads it with `sampling_params.get(\"max_new_tokens\") or 0`, so explicit negatives (not None/0) reach the check.","commonSituations":"Client code computes max_new_tokens as a difference (e.g. requested_len - prompt_len) that goes negative for long prompts and forwards it anyway.","solutions":["Ensure sampling_params.max_new_tokens is >= 0 (use 0 or omit it if you don't want reserved generation budget)","Compute max_new_tokens as max(0, budget - prompt_len) on the client","Validate sampling params before submitting the request"],"exampleFix":"// before\nsampling_params = {\"max_new_tokens\": budget - prompt_tokens}\n// after\nsampling_params = {\"max_new_tokens\": max(0, budget - prompt_tokens)}","handlingStrategy":"validation","validationCode":"mnt = (sampling_params or {}).get('max_new_tokens') or 0\nif mnt < 0:\n    raise ValueError('max_new_tokens must be >= 0')","typeGuard":"def valid_max_new_tokens(sp: dict) -> bool:\n    mnt = sp.get('max_new_tokens') or 0\n    return isinstance(mnt, int) and mnt >= 0","tryCatchPattern":null,"preventionTips":["Clamp computed max_new_tokens with max(0, ...)","Treat missing/None as 0 rather than forwarding negatives"],"tags":["multimodal","sampling-params","valueerror"],"backgroundTag":"invalid-config-parameter","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}