warpdotdev/warp · error

Invalid repo format: '{}'. Expected format: 'owner/repo'

Error message

Invalid repo format: '{}'. Expected format: 'owner/repo'

What it means

Thrown by parse_repos() in app/src/ai/agent_sdk/environment.rs when a --repo argument cannot be split on '/' into exactly two non-empty parts ('owner' and 'repo'). The value is converted into a GithubRepo via GithubRepo::new(parts[0], parts[1]), so any string that is not a bare 'owner/repo' slug is rejected before any network or Warp Drive work starts. It surfaces as a fatal CLI error from the environment create/update commands, which call parse_repos on the repo and remove_repo arguments.

Source

Thrown at app/src/ai/agent_sdk/environment.rs:48

use crate::cloud_object::{CloudObject, CloudObjectLookup as _};
use crate::server::cloud_objects::update_manager::{
    ObjectOperation, OperationSuccessType, UpdateManager, UpdateManagerEvent,
};
use crate::server::ids::{ClientId, ServerId, SyncId};
use crate::server::server_api::ServerApiProvider;
use crate::util::time_format::format_approx_duration_from_now_utc;
use crate::workspaces::user_profiles::UserProfiles;

const WARP_DEV_ENVIRONMENTS_REPO: &str = "https://github.com/warpdotdev/warp-dev-environments";

/// Parse repo strings in the format "owner/repo" into GithubRepo objects.
fn parse_repos(repo_strings: Vec<String>) -> anyhow::Result<Vec<GithubRepo>> {
    repo_strings
        .into_iter()
        .map(|r| {
            let parts: Vec<&str> = r.split('/').collect();
            if parts.len() != 2 {
                return Err(anyhow::anyhow!(
                    "Invalid repo format: '{}'. Expected format: 'owner/repo'",
                    r
                ));
            }
            Ok(GithubRepo::new(parts[0].to_string(), parts[1].to_string()))
        })
        .collect()
}

/// Handle environment-related CLI commands.
pub fn run(
    ctx: &mut AppContext,
    global_options: GlobalOptions,
    command: EnvironmentCommand,
) -> anyhow::Result<()> {
    let runner = ctx.add_singleton_model(|_ctx| EnvironmentCommandRunner);
    match command {
        EnvironmentCommand::List => {

View on GitHub (pinned to e72fd7aacb)

Solutions

  1. Pass the bare slug: `--repo owner/repo` (e.g. `--repo warpdotdev/warp-dev-environments`), not a URL or SSH remote
  2. If your source is a URL, strip the scheme and host first (keep only the last two path segments)
  3. Check for trailing/leading slashes and empty strings in scripted arguments before invoking the CLI
  4. If you must accept URLs in tooling, pre-validate with a split('/').len() == 2 check and normalize to owner/repo

Example fix

# before
warp environment create --name dev --repo https://github.com/warpdotdev/warp-dev-environments

# after
warp environment create --name dev --repo warpdotdev/warp-dev-environments
Defensive patterns

Strategy: validation

Validate before calling

// Rust: validate before calling the CLI/parse_repos
fn is_valid_repo_spec(s: &str) -> bool {
    let parts: Vec<&str> = s.split('/').collect();
    parts.len() == 2 && !parts[0].is_empty() && !parts[1].is_empty()
}

// Normalize a pasted URL to owner/repo before passing it
fn normalize_repo_input(s: &str) -> Option<String> {
    let slug = s.trim().trim_start_matches("https://github.com/")
        .trim_start_matches("http://github.com/");
    let parts: Vec<&str> = slug.split('/').collect();
    if parts.len() >= 2 {
        Some(format!("{}/{}", parts[parts.len() - 2], parts[parts.len() - 1]))
    } else {
        None
    }
}

Type guard

fn is_owner_repo_slug(s: &str) -> bool {
    matches!(s.split('/').collect::<Vec<_>>()[..], [o, r] if !o.is_empty() && !r.is_empty())
}

Try / catch

// parse_repos returns anyhow::Result — handle it at the call site
match parse_repos(repo_strings) {
    Ok(repos) => { /* proceed */ },
    Err(err) => eprintln!("bad --repo argument: {err}"),
}

Prevention

When it happens

Trigger: Running `warp environment create --repo <value>` or `warp environment update --repo <value>` where value is a full GitHub URL (e.g. https://github.com/warpdotdev/warp-dev-environments splits into 5+ parts), a bare repo name ('myrepo' splits into 1 part), a trailing/leading slash ('owner/repo/' splits into 3 parts), or an empty string.

Common situations: Users pasting a repo URL copied from the browser instead of the owner/repo slug; scripts that pass git remotes (git@github.com:owner/repo.git) verbatim; shell quoting that swallows or adds slashes; trailing slash after tab-completion.

Related errors


AI-assisted analysis of warpdotdev/warp@e72fd7aacb (2026-08-16). Data as JSON: /api/errors/c36c53ec8323e69a. Report an issue: GitHub.