jdx/mise · error
invalid relay path encoding
Error message
invalid relay path encoding
What it means
`validate_path` rejects relay request paths containing a `%` that is not followed by two hex digits (or the `%` sits at the very end of a segment). Because the relay later percent-decodes segments, a malformed escape would either fail decoding or allow a second decoder downstream to reinterpret the path, so it is rejected up front as a security guard against scope bypass.
Solutions
- Percent-encode the path properly so every `%` becomes `%25` and real escapes use two hex digits
- Remove or escape the stray `%` character from the requested path/ref/filename
- If a proxy rewrote the URL, disable the rewriting or re-encode the path before it reaches the relay
Example fix
// before let path = "/web/org/repo/archive/refs/tags/v1.0%.tar.gz"; // after let path = "/web/org/repo/archive/refs/tags/v1.0%25.tar.gz";
Defensive patterns
Strategy: validation
Validate before calling
fn has_valid_encoding(path: &str) -> bool {
path.split('/').all(|seg| {
seg.bytes().enumerate().all(|(i, b)| {
b != b'%' || seg.as_bytes().get(i+1..i+3)
.is_some_and(|d| d.iter().all(u8::is_ascii_hexdigit))
})
})
} Try / catch
match authorize(&scope, method, path, query) {
Err(e) if e.to_string().contains("invalid relay path encoding") => {
eprintln!("path contains a malformed % escape: {path}");
}
Ok(t) => forward(t),
Err(e) => return Err(e),
} Prevention
- Use a URL-encoding library rather than hand-building paths
- Never splice raw user/filename strings containing % into URLs
- Avoid double-encoding; encode once at URL construction time
- Beware proxies that rewrite or truncate percent-escapes
When it happens
Trigger: Any relayed request path segment containing a bare `%` not forming a valid `%XX` escape: e.g. `/git/org/repo/info/refs` with a literal `%` in a ref name, a truncated escape like `%A`, `%ZZ`, or a `%` at the end of a segment such as `/api/repos/org/re%`.
Common situations: A tool or script passes a raw filename containing `%` (common in generated URLs, logs, or Windows-encoded names) into an archive or contents path; a truncated double-encoding (`%25` became `%`) after an intermediate proxy rewrote the URL.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- invalid relay path
- expected a GitHub repository in OWNER/REPO form
- GitHub relay is not connected
- GitHub relay permits read-only repository operations only
- missing archive reference
AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17).
Data as JSON: /api/errors/c5fdcbb3bfb7150b.
Report an issue: GitHub.
Appendix: source
Thrown at src/github_relay.rs:180
struct Target {
url: String,
git: bool,
archive_repo: Option<String>,
}
#[cfg(any(unix, test))]
fn validate_path(path: &str) -> Result<()> {
// Validate decoded segments, retaining the original spelling upstream. Reject
// encoded separators and percent signs so a second decoder cannot change scope.
for segment in path.split('/') {
for (index, byte) in segment.bytes().enumerate() {
if byte == b'%'
&& !segment
.as_bytes()
.get(index + 1..index + 3)
.is_some_and(|digits| digits.iter().all(u8::is_ascii_hexdigit))
{
bail!("invalid relay path encoding");
}
}
let decoded = urlencoding::decode(segment)?;
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();View on GitHub (pinned to 533346cc37)