{"record":{"id":"79a435b8ece5f916","repo":"NousResearch/hermes-agent","slug":"anthropic-messages-adapter-requires-a-positive-max","errorCode":null,"errorMessage":"Anthropic Messages adapter requires a positive max_tokens value for model {model!r}; got {requested!r} and no model default resolved.","messagePattern":"Anthropic Messages adapter requires a positive max_tokens value for model (.+?); got (.+?) and no model default resolved\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/anthropic_adapter.py","lineNumber":258,"sourceCode":"    Prefers ``requested`` when it is a positive finite number; otherwise\n    falls back to the model's output ceiling. Raises ``ValueError`` if no\n    positive budget can be resolved (should not happen with current model\n    table defaults, but guards against a future regression where\n    ``_get_anthropic_max_output`` could return ``0``).\n\n    Separately, callers apply a context-window clamp — this resolver does\n    not, to keep the positive-value contract independent of endpoint\n    specifics.\n\n    Ported from openclaw/openclaw#66664 (resolveAnthropicMessagesMaxTokens).\n    \"\"\"\n    resolved = _resolve_positive_anthropic_max_tokens(requested)\n    if resolved is not None:\n        return resolved\n    fallback = _get_anthropic_max_output(model)\n    if fallback > 0:\n        return fallback\n    raise ValueError(\n        f\"Anthropic Messages adapter requires a positive max_tokens value for \"\n        f\"model {model!r}; got {requested!r} and no model default resolved.\"\n    )\n\n\ndef _supports_adaptive_thinking(model: str) -> bool:\n    \"\"\"Return True for Claude models that use adaptive thinking (4.6+).\n\n    Defaults *unknown* Claude models to adaptive (the modern contract) and\n    only returns False for the explicit legacy list of older Claude families\n    that require manual budget-based thinking. Non-Claude Anthropic-Messages\n    models (minimax, qwen3, …) return False so they keep the manual path.\n\n    Kimi / Moonshot models are the exception: their Anthropic-compatible\n    endpoints implement the adaptive contract (``thinking.type=\"adaptive\"``\n    + ``output_config.effort``, including ``xhigh`` and ``display``).\n    \"\"\"\n    if _model_name_is_kimi_family(model):","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/anthropic_adapter.py#L240-L276","documentation":"The Anthropic Messages adapter requires a positive max_tokens on every call. This ValueError fires when the caller passed a non-positive value (0/None/negative) AND _get_anthropic_max_output(model) resolved no positive default for that model name. The resolver deliberately does no context-window clamping so the positive-value contract stays independent of endpoint specifics.","triggerScenarios":"Building/converting an Anthropic Messages request with max_tokens=0, None, or a negative number for a model that has no entry in the max-output table, so the fallback returns 0.","commonSituations":"A new or renamed Claude model not yet present in _get_anthropic_max_output's table while call-site code passes 0 meaning 'unlimited'; config sets max_tokens: 0 expecting a provider default; a caller copying chat-completions semantics where 0 can mean unset.","solutions":["Pass an explicit positive max_tokens (e.g. 16384) at the call site","If 'use the model default' was intended, omit the value entirely rather than zeroing it","Add or update the model's default in the max-output resolution table so the fallback is positive","Upgrade to a release where the new model's default is catalogued"],"exampleFix":"# before\nbuild_anthropic_request(messages=msgs, max_tokens=0)\n\n# after\nbuild_anthropic_request(messages=msgs, max_tokens=16384)","handlingStrategy":"validation","validationCode":"def valid_max_tokens(value) -> bool:\n    return isinstance(value, int) and not isinstance(value, bool) and value > 0\n\nif max_tokens is not None:\n    assert valid_max_tokens(max_tokens), (\n        f\"Anthropic Messages requires positive max_tokens, got {max_tokens!r}\"\n    )","typeGuard":"from typing import Any\n\ndef is_positive_int(value: Any) -> bool:\n    \"\"\"Narrows to a positive, non-bool integer usable as Anthropic max_tokens.\"\"\"\n    return isinstance(value, int) and not isinstance(value, bool) and value > 0","tryCatchPattern":"try:\n    payload = build_anthropic_messages_request(messages, max_tokens=max_tokens)\nexcept ValueError as e:\n    if \"positive max_tokens\" in str(e):\n        payload = build_anthropic_messages_request(messages, max_tokens=16384)\n    else:\n        raise","preventionTips":["Never pass 0/None as 'unlimited' to the Anthropic Messages API — omit the value instead","Keep the max-output default table updated when adopting new Claude model names","Unit-test request builders with max_tokens in {None, 0, -1} to catch regressions early"],"tags":["anthropic","max-tokens","validation","adapter"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}