jdx/mise · error
invalid relay path encoding
Error message
invalid relay path encoding
What it means
`validate_path` percent-decodes each segment of the relayed URL path. Before decoding it checks that every `%` is followed by exactly two ASCII hex digits; a bare or malformed `%` (e.g. `100%`, `%ZZ`) makes the segment undecodable and fails with this error, preventing ambiguous or smuggling-style paths from reaching the upstream host.
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 afd2eddd3a)
Solutions
- Percent-encode the segment properly with `urlencoding::encode` (a literal `%` becomes `%25`).
- Remove or rename the ref/tag containing the stray `%` character.
- Verify the URL wasn't double-encoded; decode once yourself before passing the path.
Example fix
// before
let path = format!("/api/repos/{owner}/{repo}/tarball/{tag}"); // tag = "v1.0%"
// after
let path = format!("/api/repos/{owner}/{repo}/tarball/{}", urlencoding::encode(tag)); Defensive patterns
Strategy: validation
Validate before calling
fn segment_encodes_cleanly(seg: &str) -> bool {
let b = seg.as_bytes();
(0..b.len()).all(|i| b[i] != b'%' || (i + 2 < b.len()
&& b[i+1].is_ascii_hexdigit() && b[i+2].is_ascii_hexdigit()))
}
let encoded = urlencoding::encode(raw_segment); // encode % as %25 before building the path Try / catch
match relay::authorize(&scope, "GET", path, None) {
Err(e) if e.to_string().contains("path encoding") => eprintln!("percent-encode segment: {e}"),
Err(e) => return Err(e),
Ok(t) => t,
} Prevention
- Always percent-encode dynamic path segments with `urlencoding::encode` before composing URLs.
- Never paste percent-encoded strings from logs (double encoding); encode exactly once.
- Reject refs/tags containing `%` at input time rather than at request time.
When it happens
Trigger: Calling `authorize` or `archive_redirect` with a path segment containing `%` not forming a valid percent-escape — e.g. `/api/repos/owner/repo/tarball/100%done` or `/git/owner/repo.git/info%2refs`.
Common situations: Refs, tags, or file names containing literal `%` that were not percent-encoded (`v1.0%`); double-encoded values pasted from logs (`%2520`); building the path with an unencoded user-supplied string.
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
- unsupported relay destination
- setup repository URL must be nonempty and must not start wit
- invalid HTTP setup repository URL; use a Git credential help
- brew-cask: invalid {kind} '{value}'
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/c5fdcbb3bfb7150b.
Report an issue: GitHub.