aaif-goose/goose · error

Invalid repo id '{}': expected owner/name

Error message

Invalid repo id '{}': expected owner/name

What it means

split_repo_id requires a '/' in the repo id (split_once('/')) and is used by model_repo and download_gguf_to_hf_cache to build HF API/download requests. Bare names or empty strings fail here. Note only the presence of a slash is checked: '/name' or 'a/b/c' pass this check with empty or compound parts and fail later at the HF API with a 404-style error instead.

Source

Thrown at crates/goose-local-inference/src/hf_models.rs:1510

}

async fn hf_client() -> Result<HFClient> {
    let mut builder = HFClient::builder().user_agent("goose-ai-agent");
    if let Some(token) = optional_hf_token(huggingface_auth::resolve_token_async()).await {
        builder = builder.token(token);
    }
    builder.build().map_err(Into::into)
}

fn model_repo(client: &HFClient, repo_id: &str) -> Result<HFRepository<RepoTypeModel>> {
    let (owner, name) = split_repo_id(repo_id)?;
    Ok(client.model(owner, name))
}

fn split_repo_id(repo_id: &str) -> Result<(&str, &str)> {
    repo_id
        .split_once('/')
        .ok_or_else(|| anyhow::anyhow!("Invalid repo id '{}': expected owner/name", repo_id))
}

async fn search_mlx_models(query: &str, limit: usize) -> Result<Vec<HfModelInfo>> {
    let mut results = search_mlx_models_with_query(query, limit).await?;
    if !query.contains('/') {
        results
            .extend(search_mlx_models_with_query(&format!("mlx-community/{query}"), limit).await?);
        results.extend(search_mlx_models_with_query(&format!("google/{query}"), limit).await?);
    }
    dedupe_models(&mut results);
    results.truncate(limit);
    Ok(results)
}

async fn search_mlx_models_with_query(query: &str, limit: usize) -> Result<Vec<HfModelInfo>> {
    let client = hf_client().await?;
    let stream = client
        .list_models()

View on GitHub (pinned to 3810898a74)

Solutions

  1. Use the full 'owner/name' id exactly as it appears in the huggingface.co URL, e.g. 'bartowski/Llama-3.2-1B-Instruct-GGUF'
  2. If you only have a bare name, resolve it through model search first to obtain the full id
  3. Normalize/validate ids at your config boundary so partial ids never reach the resolver

Example fix

// before
let repo = client.model(repo_id, String::new()); // or passing a bare name downstream

// after: validate shape once at the edge
let (owner, name) = repo_id
    .split_once('/')
    .filter(|(o, n)| !o.is_empty() && !n.is_empty() && !n.contains('/'))
    .with_context(|| format!("expected 'owner/name', got '{repo_id}'"))?;
Defensive patterns

Strategy: validation

Validate before calling

anyhow::ensure!(
    matches!(repo_id.split_once('/'), Some((o, n)) if !o.is_empty() && !n.is_empty() && !n.contains('/')),
    "repo id must be 'owner/name', got '{repo_id}'"
);

Type guard

fn is_valid_repo_id(repo_id: &str) -> bool {
    matches!(repo_id.split_once('/'), Some((owner, name)) if !owner.is_empty() && !name.is_empty() && !name.contains('/'))
}

Try / catch

match split_repo_id_result {
    Err(e) if e.to_string().contains("expected owner/name") => {
        // resolve the bare name through model search to get the full id
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling the GGUF download/resolve path with a bare model name like 'Llama-3.2-1B-Instruct-GGUF', an empty string, or a value trimmed of its owner prefix by UI code.

Common situations: Search-driven flows returning bare names; configs storing only the short title; copy-paste from HF URLs that drops the owner segment.

Related errors


AI-assisted analysis of aaif-goose/goose@3810898a74 (2026-08-16). Data as JSON: /api/errors/c3dd82beb7547d35. Report an issue: GitHub.