BoundaryML/baml · error

Failed to parse client options for {}: {}

Error message

Failed to parse client options for {}:
{}

What it means

Thrown by ClientRegistry when a client definition's options (properties) fail provider-specific deserialization. The runtime resolves each key/value into typed options via the provider's parse_client_property, collecting all per-field validation messages. This means the client's options block in baml.config or the dynamic ClientSpec does not match what the provider expects (wrong types, unknown keys, missing required fields).

Source

Thrown at engine/baml-runtime/src/client_registry/mod.rs:64

            name: format!("{provider}/{model}"),
            provider: provider.clone(),
            retry_policy: None,
            options: vec![("model".to_string(), BamlValue::String(model.to_string()))]
                .into_iter()
                .collect(),
        }
    }

    pub fn unresolved_options(&self) -> Result<UnresolvedClientProperty<()>> {
        let property = PropertyHandler::new(
            self.options
                .iter()
                .map(|(k, v)| Ok((k.clone(), ((), v.to_resolvable()?))))
                .collect::<Result<_>>()?,
            (),
        );
        self.provider.parse_client_property(property).map_err(|e| {
            anyhow::anyhow!(
                "Failed to parse client options for {}:\n{}",
                self.name,
                e.into_iter()
                    .map(|e| e.message)
                    .collect::<Vec<_>>()
                    .join("\n")
            )
        })
    }
}

#[derive(Clone, Deserialize, Debug, PartialEq)]
pub struct ClientRegistry {
    #[serde(deserialize_with = "deserialize_clients")]
    clients: HashMap<String, ClientProperty>,
    primary: Option<String>,
}

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Read the joined field messages in the error to find which option key failed, then fix its value type in the options map (e.g. pass temperature as a number, not a string).
  2. Check the provider-specific option names against the BAML docs for that provider (openai/azure/ollama/responses/transcriptions differ).
  3. If building from JSON, parse numeric/boolean fields into real types before calling the dynamic constructor (e.g. serde_json::from_value into a typed struct first).
  4. Upgrade/align the BAML version if the option was renamed in a recent release.

Example fix

// before
let options = json!({ "model": "gpt-4o", "temperature": "0.7" });
let client = runtime.registry().dynamic_new(ctx, "my-gpt", options)?;
// after
let options = json!({ "model": "gpt-4o", "temperature": 0.7 });
let client = runtime.registry().dynamic_new(ctx, "my-gpt", options)?;
Defensive patterns

Strategy: validation

Validate before calling

fn validate_client_options(options: &serde_json::Map<String, serde_json::Value>) -> Result<(), String> {
    let numeric = ["temperature", "max_tokens", "top_p", "seed"];
    for k in numeric {
        if let Some(v) = options.get(*k) {
            if !v.is_number() {
                return Err(format!("option `{}` must be a number, got: {:?}", k, v));
            }
        }
    }
    if !options.contains_key("model") {
        return Err("option `model` is required".to_string());
    }
    Ok(())
}

Type guard

fn is_valid_options(v: &serde_json::Value) -> bool {
    v.is_object() && v.get("model").map_or(false, |m| m.is_string())
}

Try / catch

match registry.dynamic_new(ctx, name, options) {
    Ok(c) => c,
    Err(e) if e.to_string().contains("Failed to parse client options") => {
        eprintln!("Bad client options for {}: {e}", name);
        return Err(e);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling any dynamic client constructor (dynamic_new, dynamic_new_generic, dynamic_new_ollama, dynamic_new_azure, dynamic_new_responses, dynamic_new_transcriptions) with an options map whose values cannot be converted with to_resolvable or parsed by the provider's property parser; e.g. passing a string where a number (temperature, max_tokens) is required, or an unrecognized option key.

Common situations: Building clients at runtime from JSON/env-driven config where option values are strings instead of typed values; typos in option names for a provider (e.g. Azure deployment settings); upgrading BAML when a provider renames or drops an option; mixing options intended for one provider (OpenAI) into another (Ollama/Azure).

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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