BoundaryML/baml · error

default_role must be one of 'system', 'user' or 'assistant':

Error message

default_role must be one of 'system', 'user' or 'assistant': {}. Please specify "allowed_roles" if you want to use other custom default role.

What it means

When a BAML LLM client config specifies a `default_role` but no `allowed_roles`, only the built-in chat roles 'system', 'user', and 'assistant' are accepted. Any other default role string is rejected at client spec resolution time. To use a custom default role, you must explicitly list it in `allowed_roles`.

Source

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

            .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,
                            allowed
                        ));
                    }
                }
            }
            (None, Some(remap)) => {

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Change default_role to one of 'system', 'user', or 'assistant'.
  2. Add an allowed_roles array that includes the custom default role you want.
  3. Fix casing/typos in the default_role value (roles are matched exactly).

Example fix

// before
client<llm> MyClient {
  provider openai
  default_role "developer"
}
// after
client<llm> MyClient {
  provider openai
  default_role "system"
}
// or, to keep the custom role:
client<llm> MyClient {
  provider openai
  allowed_roles ["system", "user", "assistant", "developer"]
  default_role "developer"
}
Defensive patterns

Strategy: validation

Validate before calling

const DEFAULT_ROLES = ["system", "user", "assistant"];
if (!allowedRoles && defaultRole && !DEFAULT_ROLES.includes(defaultRole)) {
  throw new Error(`default_role '${defaultRole}' requires allowed_roles`);
}

Prevention

When it happens

Trigger: Resolving a client spec where allowed_roles is unset and default_role is a string other than 'system', 'user', or 'assistant' (e.g. 'developer', 'function').

Common situations: Copying client configs from providers whose role vocabulary differs from OpenAI's (e.g. newer role names), or typos like 'System'/'users' in BAML client blocks.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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