jdx/mise · error
GitHub relay permits read-only repository operations only
Error message
GitHub relay permits read-only repository operations only
What it means
The GitHub relay is strictly read-only: `authorize` matches the method + path against a small allow-list (git info/refs upload-pack GET, API repo GETs, release/archive downloads with GET|HEAD, etc.) and fails with this error for anything else. Write operations (POST/PUT/PATCH/DELETE) and non-allow-listed read endpoints are rejected before any network call.
Source
Thrown at src/github_relay.rs:229
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,
"contents" | "releases" | "tags" | "branches" | "tarball" | "zipball"
) && matches!(method, "GET" | "HEAD")
}
["web", _, _, "releases", "download", _, ..] => matches!(method, "GET" | "HEAD"),
["web", _, _, "archive", _, ..] => matches!(method, "GET" | "HEAD"),
_ => false,
};
if !allowed {
bail!("GitHub relay permits read-only repository operations only");
}
if !git && let Some(query) = query {
for (key, _) in url::form_urlencoded::parse(query.as_bytes()) {
if !matches!(key.as_ref(), "ref" | "page" | "per_page") {
bail!("unsupported query parameter");
}
}
}
let host = if p[0] == "api" {
"api.github.com"
} else {
"github.com"
};
let suffix = path.split_once('/').expect("validated path").1;
let mut url = format!("https://{host}/{suffix}");
let archive_repo = match p.as_slice() {
["api", "repos", _, _, "tarball" | "zipball", ..] => Some(name.clone()),
["web", _, _, "archive", rest @ ..] => {View on GitHub (pinned to afd2eddd3a)
Solutions
- Use only read-only operations: GET (and HEAD for downloads) against allowed paths — clone/fetch via git upload-pack, tarball/zipball downloads, repo metadata reads.
- Remove write calls (releases, issues, push) from code that runs through the relay; perform them outside the relay with proper credentials if truly needed.
- Check the method constant passed to `forward`/`operation` — a wrong default (e.g. POST for a metadata fetch) will be rejected.
Example fix
// before relay::forward(&scope, "POST", "/api/repos/o/r/releases", Some(body))?; // after relay::forward(&scope, "GET", "/api/repos/o/r/releases/latest", None)?;
Defensive patterns
Strategy: validation
Validate before calling
fn is_read_only(method: &str) -> bool {
matches!(method, "GET" | "HEAD")
}
// guard before calling forward/operation
assert!(is_read_only("GET")); Try / catch
match relay::forward(&scope, method, path, None) {
Err(e) if e.to_string().contains("read-only") => eprintln!("{method} {path} is not permitted by the relay"),
Err(e) => return Err(e),
Ok(resp) => resp,
} Prevention
- Audit GitHub client code for POST/PUT/PATCH/DELETE calls; move them out of relayed code paths.
- Use clone/fetch (upload-pack) only; never route git push through the relay.
- Default HTTP clients to GET for metadata and downloads.
When it happens
Trigger: Calling `forward`/`operation` with a mutating method (e.g. POST to create a release, DELETE a file), or a method/path combo not in the allow-list such as `POST /api/repos/o/r/issues` or `GET /git/o/r/git-receive-pack`.
Common situations: Pointing a generic GitHub API client (which may POST or PATCH) through the relay; attempting `git push` (receive-pack) instead of clone/fetch; tooling that creates comments, releases, or checks while installing dependencies.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- repository is outside the approved relay scope
- unsupported GitHub operation
- unsupported query parameter
- unsupported archive format
- unsupported relay host
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/332a4b41aa6b3a55.
Report an issue: GitHub.