BoundaryML/baml · error

Failed before LLM call (user error)

Error message

Failed before LLM call (user error): {message}

What it means

LLMResponse::content() also distinguishes failures that happened before any LLM call was made due to a user error; the UserFailure variant carries a message (typically from a BAML assert/raise, or argument/pre-call validation performed by user logic), and content() surfaces it verbatim as 'Failed before LLM call (user error)'. It separates user-induced pre-call failures from LLM/network failures.

Solutions

  1. Read the embedded message — it states the user-level reason the call was aborted
  2. Fix the inputs or the failing assert/raise condition in the BAML function before retrying
  3. Handle the UserFailure variant explicitly instead of relying on content()
  4. Review prompt/input validation logic that raises user failures

Example fix

// before
let text = response.content()?; // panics into generic user-error message
// after
if let LLMResponse::UserFailure(msg) = &response {
    eprintln!("pre-call user failure: {msg}"); // fix input/assert, then retry
    return Err(UserFacingError(msg.clone()));
}
let text = response.content()?;
Defensive patterns

Strategy: type-guard

Type guard

fn is_user_failure(r: &LLMResponse) -> Option<&str> {
    match r { LLMResponse::UserFailure(m) => Some(m), _ => None }
}

Try / catch

match llm_response {
    LLMResponse::UserFailure(msg) => Err(UserError(msg.clone())), // fix inputs/asserts, no retry
    other => other.content().map(|s| s.to_string()),
}

Prevention

When it happens

Trigger: Calling .content() on an LLMResponse::UserFailure — produced when user-level checks (e.g. BAML raise/assert semantics or explicit user failure injection) short-circuit before the provider request is sent.

Common situations: BAML functions with assert/raise clauses whose conditions failed on the inputs; application logic deliberately marking the call as failed before sending; misconfigured prompts/parameters caught by pre-call validation.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/24a2d27391682a32. Report an issue: GitHub.

Appendix: source

Thrown at engine/baml-runtime/src/internal/llm_client/mod.rs:201

    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Success(response) => write!(f, "{response}"),
            Self::LLMFailure(failure) => write!(f, "LLM call failed: {failure:?}"),
            Self::UserFailure(message) => {
                write!(f, "Failed before LLM call (user error): {message}")
            }
            Self::InternalFailure(message) => write!(f, "Failed before LLM call: {message}"),
            Self::Cancelled(message) => write!(f, "Operation cancelled: {message}"),
        }
    }
}

impl LLMResponse {
    pub fn content(&self) -> Result<&str> {
        match self {
            Self::Success(response) => Ok(&response.content),
            Self::LLMFailure(failure) => Err(anyhow::anyhow!("LLM call failed: {failure:?}")),
            Self::UserFailure(message) => Err(anyhow::anyhow!(
                "Failed before LLM call (user error): {message}"
            )),
            Self::InternalFailure(message) => {
                Err(anyhow::anyhow!("Failed before LLM call: {message}"))
            }
            Self::Cancelled(message) => Err(anyhow::anyhow!("Operation cancelled: {message}")),
        }
    }
}

#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct LLMErrorResponse {
    pub client: String,
    pub model: Option<String>,
    pub prompt: RenderedPrompt,
    pub request_options: BamlMap<String, serde_json::Value>,
    #[cfg_attr(target_arch = "wasm32", serde(skip_serializing))]
    pub start_time: web_time::SystemTime,

View on GitHub (pinned to bd85ce9dee)