{"record":{"id":"4ab90b748eaca2cf","repo":"sgl-project/sglang","slug":"max-tokens-must-be-positive","errorCode":null,"errorMessage":"max_tokens must be positive","messagePattern":"max_tokens must be positive","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"python/sglang/srt/entrypoints/anthropic/protocol.py","lineNumber":393,"sourceCode":"    top_k: Optional[int] = None\n    top_p: Optional[float] = None\n    # Claude 4.7 fields. The Anthropic SDK / Claude Code attach these even\n    # when targeting non-Anthropic backends, so the schema must accept them.\n    output_config: Optional[AnthropicOutputConfig] = None\n    betas: Optional[list[str]] = None\n\n    @field_validator(\"model\")\n    @classmethod\n    def _validate_model(cls, v):\n        if not v:\n            raise ValueError(\"Model is required\")\n        return v\n\n    @field_validator(\"max_tokens\")\n    @classmethod\n    def _validate_max_tokens(cls, v):\n        if v <= 0:\n            raise ValueError(\"max_tokens must be positive\")\n        return v\n\n\n# ---------- Stream deltas ----------\n# Content-block deltas (discriminated by ``type``) vs message-end delta\n# (separate model; the wire format does not put ``type`` inside its payload).\n\n\nclass TextDelta(BaseModel):\n    type: Literal[\"text_delta\"] = \"text_delta\"\n    text: str\n\n\nclass InputJsonDelta(BaseModel):\n    type: Literal[\"input_json_delta\"] = \"input_json_delta\"\n    partial_json: str\n\n","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/entrypoints/anthropic/protocol.py#L375-L411","documentation":"Raised by the pydantic field validator on MessagesRequest when max_tokens is <= 0. The Anthropic API requires max_tokens (it is mandatory, not optional), and it must be a positive integer; otherwise the request is rejected with a 400 before inference.","triggerScenarios":"POST /v1/messages with max_tokens: 0, a negative value, or a computed value that evaluates to 0 (e.g. budget math or truncation arithmetic).","commonSituations":"Clients porting from OpenAI where max_tokens is optional and defaulting it to 0; dynamic computation like max_tokens = remaining_context - overflow that underflows to 0 or negative.","solutions":["Set max_tokens to a positive integer (e.g. 1024+)","If computing max_tokens dynamically, clamp it to a minimum of 1 (preferably more)","Remember the Anthropic endpoint requires max_tokens; unlike OpenAI there is no unlimited default"],"exampleFix":"// before\n{\"model\": \"m\", \"max_tokens\": 0, \"messages\": [...]}\n// after\n{\"model\": \"m\", \"max_tokens\": 1024, \"messages\": [...]}","handlingStrategy":"validation","validationCode":"max_tokens = max(1, int(max_tokens)) if max_tokens else 1024","typeGuard":"def is_valid_max_tokens(v: object) -> bool:\n    return isinstance(v, int) and not isinstance(v, bool) and v > 0","tryCatchPattern":null,"preventionTips":["Always send explicit max_tokens on Anthropic-style APIs","Clamp computed values to a sane minimum"],"tags":["anthropic","max-tokens","validation","request-validation"],"backgroundTag":"request-validation-failed","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}