tinyhumansai/openhuman · error

{}: missing `system_prompt` — custom definitions must set an

Error message

{}: missing `system_prompt` — custom definitions must set an inline string or a file path

What it means

Agent definitions loaded from disk must carry a real system prompt: if the TOML parses but system_prompt is the empty inline placeholder, load_file bails naming the file. Builtin definitions arrive with that placeholder so load_builtins can inject defaults later, which is why a file-loaded definition with an empty inline prompt is always an authoring mistake - file definitions must set [system_prompt] inline = "..." or [system_prompt] file = "...".

Source

Thrown at src/openhuman/agent/harness/definition_loader.rs:110

/// Load a single TOML file as an [`AgentDefinition`]. Stamps `source` to
/// the absolute path.
///
/// Rejects definitions that omit (or leave blank) their `system_prompt`
/// — built-in agents are loaded separately and have their prompts
/// injected by [`crate::openhuman::agent::registry::agents::load_builtins`], so a
/// file-loaded definition that arrives with the
/// [`defaults::empty_inline_prompt`] placeholder is always a caller
/// mistake. Custom definitions must set either
/// `[system_prompt] inline = "…"` or `[system_prompt] file = "…"`.
pub fn load_file(path: &Path) -> Result<AgentDefinition> {
    let content =
        fs::read_to_string(path).with_context(|| format!("reading {}", path.display()))?;
    let mut def: AgentDefinition = toml::from_str(&content)
        .with_context(|| format!("parsing {} as AgentDefinition TOML", path.display()))?;
    if let PromptSource::Inline(body) = &def.system_prompt {
        if body.is_empty() {
            bail!(
                "{}: missing `system_prompt` — custom definitions must set an inline string \
                 or a file path",
                path.display()
            );
        }
    }
    def.source = DefinitionSource::File(path.to_path_buf());
    Ok(def)
}

fn user_home_agents_dir() -> Option<PathBuf> {
    // Honour OPENHUMAN_HOME first if set; otherwise ~/.openhuman.
    if let Ok(custom) = std::env::var("OPENHUMAN_HOME") {
        return Some(PathBuf::from(custom).join("agents"));
    }
    match crate::openhuman::config::default_root_openhuman_dir() {
        Ok(dir) => Some(dir.join("agents")),
        Err(error) => {

View on GitHub (pinned to 7491200858)

Solutions

  1. Add a [system_prompt] section with either inline = "..." or file = "path/to/prompt.md".
  2. Reload definitions or restart so the registry picks up the fixed file.
  3. Lint agent TOMLs in CI or tooling to require a non-empty prompt for file-based definitions.

Example fix

# before (~/.openhuman/agents/my-agent.toml)
id = "my-agent"
agent_tier = "standard"

# after
id = "my-agent"
agent_tier = "standard"

[system_prompt]
inline = "You are a focused assistant for ..."
Defensive patterns

Strategy: validation

Validate before calling

let def: AgentDefinition = toml::from_str(&content)?;
if matches!(&def.system_prompt, PromptSource::Inline(body) if body.is_empty()) {
    anyhow::bail!("{}: file definitions need [system_prompt] inline or file", path.display());
}

Prevention

When it happens

Trigger: A custom agent TOML under ~/.openhuman/agents (or OPENHUMAN_HOME) omits the [system_prompt] table or sets inline to an empty string; a builtin-style template that relies on defaults injection was copied to a file without adding a prompt.

Common situations: Authoring a new file-based agent; trimming a template down and dropping the prompt block; assuming the registry will fill in a default prompt for file definitions (it will not).

Related errors


AI-assisted analysis of tinyhumansai/openhuman@7491200858 (2026-08-17). Data as JSON: /api/errors/388e9e0124dfc6e6. Report an issue: GitHub.