jdx/mise · error

unsupported GitHub operation

Error message

unsupported GitHub operation

What it means

`authorize` matches the relayed request path against a fixed set of patterns: `api/repos/{owner}/{repo}/...`, or `git/{owner}/{repo}/...` / `web/{owner}/{repo}/...`. Any other path prefix is rejected because the relay is only designed to proxy GitHub repository API, git smart-HTTP, and release/archive download traffic. This is the first structural check after path validation.

Solutions

  1. Point the client only at repository endpoints: `api/repos/OWNER/REPO/...`, `git/OWNER/REPO(.git)/...`, or `web/OWNER/REPO/...`
  2. If you need a non-repository GitHub API endpoint, use direct authenticated access instead of the scoped relay
  3. Fix the client's base URL so it includes the `repos` segment for repo API calls

Example fix

// before
GET https://relay/api/user
// after
GET https://relay/api/repos/octocat/hello-world
Defensive patterns

Strategy: validation

Validate before calling

const ALLOWED_PREFIXES: [&str; 3] = ["api/repos/", "git/", "web/"];
fn relayable(path: &str) -> bool {
    ALLOWED_PREFIXES.iter().any(|p| path.starts_with(p))
        && path.split('/').filter(|s| !s.is_empty()).count() >= 3
}

Try / catch

match authorize(&scope, method, path, query) {
    Err(e) if e.to_string().contains("unsupported GitHub operation") => {
        eprintln!("{path} is not a relay-supported endpoint; use direct GitHub access");
    }
    Ok(t) => forward(t),
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A request whose path does not start with `api/repos/`, `git/`, or `web/` — e.g. `/api/user`, `/api/orgs/x`, `/gists/1`, `/git/` with fewer than 3 segments, or a typo like `/API/repos/o/r`.

Common situations: A tool is configured to proxy all GitHub API traffic through the relay but calls a non-repository endpoint (user, org, search, gists); a client uses the wrong base path (e.g. omitting `repos` for the repo API); a git client uses a non-standard path layout.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/4a3eb55eb678ec7f. Report an issue: GitHub.

Appendix: source

Thrown at src/github_relay.rs:202

        if decoded.is_empty()
            || matches!(decoded.as_ref(), "." | "..")
            || decoded.contains(['/', '\\', '%'])
            || decoded.chars().any(char::is_control)
        {
            bail!("invalid relay path");
        }
    }
    Ok(())
}

#[cfg(any(unix, test))]
fn authorize(scope: &Scope, method: &str, path: &str, query: Option<&str>) -> Result<Target> {
    validate_path(path)?;
    let p: Vec<_> = path.split('/').collect();
    let (owner, repo) = match p.as_slice() {
        ["api", "repos", owner, repo, ..] => (*owner, *repo),
        ["git" | "web", owner, repo, ..] => (*owner, repo.strip_suffix(".git").unwrap_or(repo)),
        _ => bail!("unsupported GitHub operation"),
    };
    let name = repository(&format!("{owner}/{repo}"))?;
    if !scope.permits(&name) {
        bail!("repository is outside the approved relay scope");
    }
    let git = p[0] == "git";
    let allowed = match p.as_slice() {
        ["git", _, _, "info", "refs"] => {
            method == "GET" && query == Some("service=git-upload-pack")
        }
        ["git", _, _, "git-upload-pack"] => method == "POST" && query.is_none(),
        ["api", "repos", _, _] => method == "GET" || method == "HEAD",
        ["api", "repos", _, _, "git", kind, ..] => {
            matches!(*kind, "refs" | "matching-refs") && matches!(method, "GET" | "HEAD")
        }
        ["api", "repos", _, _, kind, ..] => {
            matches!(
                *kind,

View on GitHub (pinned to 533346cc37)