BoundaryML/baml · error

default_role must be in allowed_roles

Error message

default_role must be in allowed_roles: {}. Not found in {:?}

What it means

When resolving a strategy/role-based client, if `default_role` is set it must appear in `allowed_roles`. If allowed_roles is absent, only system/user/assistant are permitted as defaults. Violating either rule raises this error.

Solutions

  1. Add the default role to allowed_roles in the client config.
  2. Change default_role to one of the roles already in allowed_roles.
  3. If the role is a standard one (system/user/assistant), drop allowed_roles and just use a standard role name.

Example fix

// before
options {
  default_role "tool"
}
// after
options {
  allowed_roles ["system", "user", "assistant", "tool"]
  default_role "tool"
}
Defensive patterns

Strategy: validation

Validate before calling

// before resolving
if let Some(d) = &default_role {
    let allowed = allowed_roles.as_deref().unwrap_or(&["system","user","assistant"]);
    if !allowed.contains(&d.as_str()) { eprintln!("default_role '{d}' not in allowed_roles {allowed:?}"); }
}

Type guard

fn valid_default(default: &str, allowed: Option<&[String]>) -> bool {
    allowed.map_or(matches!(default, "system" | "user" | "assistant"), |a| a.iter().any(|r| r == default))
}

Prevention

When it happens

Trigger: Setting `default_role "tool"` (or another custom role) without also specifying allowed_roles containing it; setting a default_role that is missing from the allowed_roles list.

Common situations: Building a role-playing/multi-LLM strategy client with custom chat roles and forgetting allowed_roles; renaming roles in allowed_roles but not updating default_role.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at engine/baml-lib/llm-client/src/clientspec.rs:364

            .map(|remap| {
                remap
                    .iter()
                    .map(|(k, v)| Ok((k.to_string(), v.resolve(ctx)?)))
                    .collect::<Result<Vec<_>>>()
            })
            .transpose()?;

        let remap: Option<HashMap<String, String>> = remap.map(|remap| {
            remap
                .into_iter()
                .map(|(k, v)| (k.to_string(), v.to_string()))
                .collect()
        });

        match (&allowed, &default) {
            (Some(allowed), Some(default)) => {
                if !allowed.contains(default) {
                    return Err(anyhow::anyhow!("default_role must be in allowed_roles: {}. Not found in {:?}", default, allowed));
                }
            }
            (None, Some(default)) => {
                match default.as_str() {
                    "system" | "user" | "assistant" => {}
                    _ => return Err(anyhow::anyhow!("default_role must be one of 'system', 'user' or 'assistant': {}. Please specify \"allowed_roles\" if you want to use other custom default role.", default)),
                }
            }
            _ => {}
        }

        match (&allowed, &remap) {
            (Some(allowed), Some(remap)) => {
                for k in remap.keys() {
                    if !allowed.contains(k) {
                        return Err(anyhow::anyhow!(
                            "remap_role must be in allowed_roles: {}. Not found in {:?}",
                            k,

View on GitHub (pinned to bd85ce9dee)