vllm-project/vllm · error · Error

`thinking_token_budget` must be a non-negative integer or -1

Error message

`thinking_token_budget` must be a non-negative integer or -1 for unlimited.

What it means

Returned by `normalize_thinking_token_budget` (rust/src/text/src/lower.rs:204-210) when the request's `thinking_token_budget` is a negative number other than the `-1` sentinel. The contract mirrors Python vLLM's `validate_thinking_token_budget`: `None` and `-1` mean unlimited, values >= 0 pass through, any other negative value is rejected. It is a request-validation error surfaced to the API caller.

Source

Thrown at rust/src/text/src/error.rs:36

    #[error("text request `{request_id}` stop strings cannot be empty")]
    EmptyStopString { request_id: String },
    #[error(
        "this model's maximum context length is {max_model_len} tokens, \
         but the prompt contains {prompt_len} input tokens"
    )]
    PromptTooLong { max_model_len: u32, prompt_len: u32 },
    #[error(transparent)]
    Logprobs(#[from] LogprobsError),
    #[error(transparent)]
    TokenIds(#[from] TokenIdsError),
    #[error(transparent)]
    SamplingParams(#[from] SamplingParamsError),
    #[error(
        "`min_tokens` must be less than or equal to `max_tokens`, \
         got min_tokens={min_tokens}, max_tokens={max_tokens}"
    )]
    MinTokensExceedsMaxTokens { min_tokens: u32, max_tokens: u32 },
    #[error("`thinking_token_budget` must be a non-negative integer or -1 for unlimited.")]
    InvalidThinkingTokenBudget,
    #[error("invalid repetition detection params: {message}")]
    InvalidRepetitionDetection { message: String },
    #[error("text request stream `{request_id}` closed before terminal output")]
    StreamClosedBeforeTerminalOutput { request_id: String },
    #[error(transparent)]
    Llm(#[from] LlmError),
    #[error(transparent)]
    EngineCore(#[from] EngineCoreError),
}

pub type Result<T> = std::result::Result<T, Error>;

impl Error {
    /// Whether this error represents invalid user request parameters.
    pub fn is_request_validation_error(&self) -> bool {
        match self {
            Self::PromptTooLong { .. }

View on GitHub (pinned to c794754062)

Solutions

  1. Use a non-negative integer for thinking_token_budget, or -1 for unlimited
  2. Pass null/None to leave the budget unset
  3. Check client-side serialization: ensure a 'disabled' flag is not encoded as an arbitrary negative number

Example fix

// before
request.thinking_token_budget = -2; // intending 'no budget'

// after
request.thinking_token_budget = -1; // unlimited sentinel
// or
request.thinking_token_budget = null; // unset
Defensive patterns

Strategy: validation

Validate before calling

if let Some(budget) = request.thinking_token_budget {
    assert!(budget == -1 || budget >= 0,
        "thinking_token_budget must be >= 0 or -1, got {budget}");
}

Type guard

fn is_invalid_thinking_budget(e: &Error) -> bool {
    matches!(e, Error::InvalidThinkingTokenBudget)
}

Try / catch

match lower_request(params) {
    Err(Error::InvalidThinkingTokenBudget) => bad_request("thinking_token_budget must be >= 0 or -1"),
    other => other,
}

Prevention

When it happens

Trigger: Passing `thinking_token_budget: -2` (or any negative value besides -1) in a chat/completions request to the Rust frontend. Passing -1 is fine (unlimited); 0 and positives are fine.

Common situations: Clients using -1 from a different API's convention incorrectly (e.g. sending -2 or -100 to mean 'very large'); config templating that substitutes a negative default; serializing an Optional/int sentinel incorrectly from another language.

Related errors


AI-assisted analysis of vllm-project/vllm@c794754062 (2026-08-14). Data as JSON: /api/errors/b61bfce6f92e5002. Report an issue: GitHub.