sigoden/aichat · error · anyhow::Error

Unknown role

Error message

Unknown role `{name}`

What it means

Role::builtin loads an embedded role markdown asset by name ('{name}.md'); if the asset is absent, the name is not a known built-in role. The error surfaces the requested name so the caller can correct it.

Solutions

  1. Run the companion list_builtin_role_names() to see valid names and pick one
  2. Fix the typo / casing in the role name
  3. If you need a custom role, load it from a file instead of Role::builtin

Example fix

// before
let role = Role::builtin("assistent")?;
// after
let role = Role::builtin("assistant")?;
Defensive patterns

Strategy: fallback

Validate before calling

fn is_builtin_role(name: &str) -> bool {
    Role::list_builtin_role_names().iter().any(|n| n == name)
}

Try / catch

let role = Role::builtin(name)
    .or_else(|_| load_role_from_file(name))
    .with_context(|| format!("role '{}' not found; available: {:?}", name, Role::list_builtin_role_names()))?;

Prevention

When it happens

Trigger: Calling Role::builtin(name) where RolesAsset::get returns None, i.e. name doesn't match any embedded role file (exact, case-sensitive match required).

Common situations: Typo in role name in config or CLI flag; passing a custom role name to builtin instead of loading it from disk; version where the role was renamed or removed.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09). Data as JSON: /api/errors/ee7e2aa799173ef1. Report an issue: GitHub.

Appendix: source

Thrown at src/config/role.rs:96

                if let Some(value) = value.as_object() {
                    for (key, value) in value {
                        match key.as_str() {
                            "model" => role.model_id = value.as_str().map(|v| v.to_string()),
                            "temperature" => role.temperature = value.as_f64(),
                            "top_p" => role.top_p = value.as_f64(),
                            "use_tools" => role.use_tools = value.as_str().map(|v| v.to_string()),
                            _ => (),
                        }
                    }
                }
            }
        }
        role
    }

    pub fn builtin(name: &str) -> Result<Self> {
        let content = RolesAsset::get(&format!("{name}.md"))
            .ok_or_else(|| anyhow!("Unknown role `{name}`"))?;
        let content = unsafe { std::str::from_utf8_unchecked(&content.data) };
        Ok(Role::new(name, content))
    }

    pub fn list_builtin_role_names() -> Vec<String> {
        RolesAsset::iter()
            .filter_map(|v| v.strip_suffix(".md").map(|v| v.to_string()))
            .collect()
    }

    pub fn list_builtin_roles() -> Vec<Self> {
        RolesAsset::iter()
            .filter_map(|v| Role::builtin(&v).ok())
            .collect()
    }

    pub fn has_args(&self) -> bool {
        self.name.contains('#')

View on GitHub (pinned to 82976d349a)