aaif-goose/goose · error · anyhow::Error

Invalid identifier

Error message

Invalid identifier

What it means

anyhow error from the resume branch (crates/goose-cli/src/cli.rs) when the parsed Identifier has none of session_id, name, or path set. Identifier is a clap ArgGroup(required=false, multiple=false); in the resume flow an all-None identifier should normally take the 'most recent session' path, so hitting this branch means an explicitly empty identifier reached code that requires a value — effectively unreachable via the public CLI today.

Source

Thrown at crates/goose-cli/src/cli.rs:435

        if let Some(session_id) = id.session_id {
            session_id
        } else if let Some(name) = id.name {
            let sessions = session_manager.list_sessions().await?;
            sessions
                .into_iter()
                .find(|s| s.name == name || s.id == name)
                .map(|s| s.id)
                .ok_or_else(|| anyhow::anyhow!("No session found with name '{}'", name))?
        } else if let Some(path) = id.path {
            path.file_stem()
                .and_then(|s| s.to_str())
                .map(|s| s.to_string())
                .ok_or_else(|| {
                    anyhow::anyhow!("Could not extract session ID from path: {:?}", path)
                })?
        } else {
            return Err(anyhow::anyhow!("Invalid identifier"));
        }
    } else {
        let Some(id) = identifier else {
            let session = session_manager
                .create_session(
                    std::env::current_dir()?,
                    "CLI Session".to_string(),
                    SessionType::User,
                    goose_mode,
                )
                .await?;
            return Ok(Some(session.id));
        };

        if id.session_id.is_some() {
            return Err(anyhow::anyhow!("Cannot use --session-id without --resume"));
        }

View on GitHub (pinned to 3810898a74)

Solutions

  1. If calling from code, set at least one field: id.name / id.session_id / id.path
  2. Prefer letting the no-identifier path run (pass Option::None for the whole identifier) so it resolves the most recent session
  3. Keep the ArgGroup contract (required=false, multiple=false) intact when editing the CLI
  4. For scripts, pass an explicit --session-id to make intent unambiguous

Example fix

// before
let id = Identifier::default(); // all None
resolve(id).await?;

// after
let id = Identifier { name: Some("project-x".into()), ..Default::default() };
resolve(id).await?;
Defensive patterns

Strategy: validation

Validate before calling

debug_assert!(id.session_id.is_some() || id.name.is_some() || id.path.is_some(), "Identifier must carry a value in the resume per-field branch");

Type guard

fn identifier_has_value(id: &Identifier) -> bool {
    id.session_id.is_some() || id.name.is_some() || id.path.is_some()
}

Prevention

When it happens

Trigger: Programmatically constructing Identifier::default() (all fields None) and calling the resolution function directly, or a future CLI change that routes an empty identifier into the per-field match instead of the fallback.

Common situations: Unit tests or embedded callers building Identifier by hand; refactors of the clap group that change which arm executes.

Related errors


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