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
- Run the companion list_builtin_role_names() to see valid names and pick one
- Fix the typo / casing in the role name
- 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
- Validate role names against list_builtin_role_names() at startup
- Store role names as constants/enums instead of free strings
- Remember matching is exact and case-sensitive
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
- No models
- Unknown client
- Unknown model
- Please enable function calling before using the agent.
- No available model
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)