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 GitHub relay normalizes every repository reference through the `repository()` helper, which requires exactly two path segments (`OWNER/REPO`), each non-empty, not `.` or `..`, and composed only of ASCII alphanumerics, `-`, `_`, or `.`. It is thrown whenever a repository identifier passed to `from_flags`, `expand_repository`, or `authorize` does not satisfy that shape. The value is lowercased on success so scope checks are case-insensitive.

Solutions

  1. Pass exactly `OWNER/REPO` (ASCII alphanumerics, `-`, `_`, `.` only), e.g. `octocat/hello-world`
  2. Strip scheme/host/suffix first: turn `https://github.com/org/repo.git` into `org/repo` before calling
  3. Check each segment against /^[A-Za-z0-9._-]+$/ and reject `.`, `..`, or empty segments yourself before calling
  4. For request-path errors, inspect the relayed URL's repo segment for stray characters (encodings, trailing slashes, refs) and fix the requesting client

Example fix

// before
mise github-relay --scope "https://github.com/octocat/hello-world.git"
// after
mise github-relay --scope "octocat/hello-world"
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_repo(s: &str) -> bool {
    let parts: Vec<_> = s.split('/').collect();
    parts.len() == 2
        && parts.iter().all(|p| {
            !p.is_empty()
                && p != "."
                && p != ".."
                && p.bytes().all(|b| b.is_ascii_alphanumeric() || b"-_.".contains(&b))
        })
}
assert!(is_valid_repo("octocat/hello-world"));

Type guard

fn as_owner_repo(value: &str) -> Option<(&str, &str)> {
    let (owner, repo) = value.split_once('/')?;
    let ok = |s: &str| {
        !s.is_empty() && s != "." && s != ".."
            && s.bytes().all(|b| b.is_ascii_alphanumeric() || b"-_.".contains(&b))
    };
    (ok(owner) && ok(repo)).then_some((owner, repo))
}

Prevention

When it happens

Trigger: Passing something other than `owner/repo` to expand_repository (e.g. a bare repo name, a full URL, `owner/repo/extra`, or a value containing `:`, `@`, spaces, or shell-escaped characters); authorize() deriving `owner/repo` from a request path segment that contains a character outside the allowed set (e.g. a percent-encoded or dotted name like `../repo` or `owner/repo~`).

Common situations: Users configuring a relay scope write `github.com/org/repo` or an `https://...` URL instead of `org/repo`; typo'd extra slashes (`org//repo`); copy-pasting an SSH origin (`git@github.com:org/repo.git`); Git refs or archive paths containing characters the charset check rejects.

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@533346cc37 (2026-09-17). Data as JSON: /api/errors/ce170576425379dd. Report an issue: GitHub.

Appendix: 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 533346cc37)