vllm-project/vllm · error · UnifiedParserError

combined parser is constructed from split parser instances

Error message

combined parser is constructed from split parser instances

What it means

UnifiedParserError::CombinedParserConstructor is an API-misuse guard: a combined (unified) parser must be constructed through its own constructor that takes the tokenizer and shared configuration, not assembled from already-built split reasoning/tool parser instances. Building it from split instances would leave token IDs and delimiter state inconsistent, so the constructor rejects that path.

Solutions

  1. Construct the unified parser via its dedicated constructor taking tokenizer + names/config, not parser instances
  2. If you already have split parsers, keep using the split (non-unified) pipeline instead of combining them

Example fix

// before
let p = CombinedParser::from_split(reasoning_parser, tool_parser);

// after
let p = CombinedParser::new(&tokenizer, UnifiedParserConfig::new("granite"));
Defensive patterns

Strategy: type-guard

Validate before calling

// Prevent constructing the combined parser from split instances
fn wants_unified(cfg: &ParserConfig) -> bool { cfg.reasoning == cfg.tool && !cfg.reasoning.is_empty() }
if wants_unified(&cfg) { build_unified(&cfg).await } else { build_split(&cfg).await }

Type guard

fn is_combined_ctor_misuse(e: &UnifiedParserError) -> bool {
    matches!(e, UnifiedParserError::CombinedParserConstructor)
}

Try / catch

match CombinedParser::new(args) {
    Err(e @ UnifiedParserError::CombinedParserConstructor) => {
        return Err(anyhow::anyhow!("use the unified constructor with tokenizer + config, not split instances: {e}"));
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling the combined-parser constructor with arguments that resolve to split parser instances (e.g. passing pre-built ReasoningParser/ToolParser trait objects) instead of letting it construct the unified components itself.

Common situations: Library users refactoring from split parsers to the unified API and passing their old parser objects; generic code that funnels any parser pair into the combined constructor.

Related errors


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

Appendix: source

Thrown at rust/src/parser/src/unified/mod.rs:204

    /// Feed one decoded text delta into the parser, appending committed output into `output`.
    fn parse_into(&mut self, delta: &str, output: &mut UnifiedParserOutput) -> Result<()>;

    /// Flush any buffered parser state at end of stream.
    fn finish(&mut self) -> Result<UnifiedParserOutput> {
        Ok(UnifiedParserOutput::default())
    }

    /// Clear parser state and return currently uncommitted buffered text.
    fn reset(&mut self) -> String {
        String::new()
    }
}

/// Errors produced while creating or running unified parsers.
#[derive(Debug, Error, Macro)]
#[thiserror_ext(macro(path = "crate::unified", mangle))]
pub enum UnifiedParserError {
    #[error("combined parser is constructed from split parser instances")]
    CombinedParserConstructor,
    #[error("tokenizer is missing unified parser token `{token}`")]
    MissingToken { token: String },
    #[error("unified parser parsing failed: {message}")]
    ParsingFailed { message: String },
    #[error(transparent)]
    Reasoning(#[from] ReasoningError),
    #[error(transparent)]
    Tool(#[from] ToolParserError),
}

/// Returns the ID for the given token, or an error if it's not found.
fn token_id(tokenizer: &dyn vllm_tokenizer::Tokenizer, token: &str) -> Result<u32> {
    tokenizer.token_to_id(token).ok_or_else(|| UnifiedParserError::MissingToken {
        token: token.to_string(),
    })
}

View on GitHub (pinned to c794754062)