jdx/mise · error

expected a GitHub repository in OWNER/REPO form

Error message

expected a GitHub repository in OWNER/REPO form

What it means

The `repository` function in the GitHub relay normalizes and validates repository identifiers. Each OWNER and REPO segment must be non-empty, not `.` or `..`, and contain only ASCII alphanumerics, `-`, `_`, or `.`; anything else (or a missing `/` separator) yields this error. The value is lowercased on success so scope checks are case-insensitive.

Source

Thrown at src/github_relay.rs:140

        };
        scope
    };
    Ok(Some(scope))
}

fn repository(value: &str) -> Result<String> {
    let parts: Vec<_> = value.split('/').collect();
    if parts.len() != 2
        || parts.iter().any(|s| {
            s.is_empty()
                || *s == "."
                || *s == ".."
                || !s
                    .bytes()
                    .all(|b| b.is_ascii_alphanumeric() || b"-_.".contains(&b))
        })
    {
        bail!("expected a GitHub repository in OWNER/REPO form");
    }
    Ok(value.to_ascii_lowercase())
}

/// Expand only unambiguous shorthand, preserving paths and explicit transports.
pub(crate) fn expand_repository(value: &str) -> Result<String> {
    if value.contains(':')
        || value.starts_with(['/', '.', '~'])
        || std::path::Path::new(value).exists()
    {
        return Ok(value.to_string());
    }
    repository(value)?;
    Ok(format!(
        "https://github.com/{}.git",
        value.strip_suffix(".git").unwrap_or(value)
    ))
}

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Pass the repository exactly as `OWNER/REPO` with a single `/`, e.g. `octocat/hello-world`.
  2. Strip scheme, host, `.git` suffix where not supported, and any trailing path or ref before calling.
  3. Remove invalid characters (spaces, `:`, `@`, unicode) from owner and repo names.

Example fix

// before
let scope = relay::from_flags("https://github.com/octocat/hello-world")?;
// after
let scope = relay::from_flags("octocat/hello-world")?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_owner_repo(s: &str) -> bool {
    let (o, r) = match s.split_once('/') { Some(x) => x, None => return false };
    let ok = |s: &str| !s.is_empty() && s != "." && s != ".."
        && s.bytes().all(|b| b.is_ascii_alphanumeric() || b"-_..contains(&b));
    ok(o) && ok(r)
}
assert!(is_owner_repo("octocat/hello-world"));

Type guard

fn as_owner_repo(v: &str) -> Option<String> {
    let (o, r) = v.split_once('/')?;
    let ok = |s: &str| !s.is_empty() && !matches!(s, "." | "..")
        && s.bytes().all(|b| b.is_ascii_alphanumeric() || b"-_..contains(&b));
    (ok(o) && ok(r)).then(|| v.to_ascii_lowercase())
}

Try / catch

match relay::from_flags(input) {
    Err(e) if e.to_string().contains("OWNER/REPO") => eprintln!("pass owner/repo, got {input:?}"),
    Err(e) => return Err(e),
    Ok(scope) => scope,
}

Prevention

When it happens

Trigger: Calling `repository` (via `from_flags`, `expand_repository`, or `authorize`) with a value missing the `/` separator (`myrepo`), containing invalid characters (`owner/repo!`, `owner name/repo`), empty segments (`/repo`, `owner/`), or `.`/`..` segments.

Common situations: Typing a full GitHub URL instead of OWNER/REPO; including a branch or path suffix (`owner/repo/subdir`); shell quoting or whitespace inside the value; passing a shorthand like `~` or a local path.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/ce170576425379dd. Report an issue: GitHub.