{"record":{"id":"ce170576425379dd","repo":"jdx/mise","slug":"expected-a-github-repository-in-owner-repo-form","errorCode":null,"errorMessage":"expected a GitHub repository in OWNER/REPO form","messagePattern":"expected a GitHub repository in OWNER/REPO form","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/github_relay.rs","lineNumber":140,"sourceCode":"        };\n        scope\n    };\n    Ok(Some(scope))\n}\n\nfn repository(value: &str) -> Result<String> {\n    let parts: Vec<_> = value.split('/').collect();\n    if parts.len() != 2\n        || parts.iter().any(|s| {\n            s.is_empty()\n                || *s == \".\"\n                || *s == \"..\"\n                || !s\n                    .bytes()\n                    .all(|b| b.is_ascii_alphanumeric() || b\"-_.\".contains(&b))\n        })\n    {\n        bail!(\"expected a GitHub repository in OWNER/REPO form\");\n    }\n    Ok(value.to_ascii_lowercase())\n}\n\n/// Expand only unambiguous shorthand, preserving paths and explicit transports.\npub(crate) fn expand_repository(value: &str) -> Result<String> {\n    if value.contains(':')\n        || value.starts_with(['/', '.', '~'])\n        || std::path::Path::new(value).exists()\n    {\n        return Ok(value.to_string());\n    }\n    repository(value)?;\n    Ok(format!(\n        \"https://github.com/{}.git\",\n        value.strip_suffix(\".git\").unwrap_or(value)\n    ))\n}","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/github_relay.rs#L122-L158","documentation":"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.","triggerScenarios":"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~`).","commonSituations":"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.","solutions":["Pass exactly `OWNER/REPO` (ASCII alphanumerics, `-`, `_`, `.` only), e.g. `octocat/hello-world`","Strip scheme/host/suffix first: turn `https://github.com/org/repo.git` into `org/repo` before calling","Check each segment against /^[A-Za-z0-9._-]+$/ and reject `.`, `..`, or empty segments yourself before calling","For request-path errors, inspect the relayed URL's repo segment for stray characters (encodings, trailing slashes, refs) and fix the requesting client"],"exampleFix":"// before\nmise github-relay --scope \"https://github.com/octocat/hello-world.git\"\n// after\nmise github-relay --scope \"octocat/hello-world\"","handlingStrategy":"validation","validationCode":"fn is_valid_repo(s: &str) -> bool {\n    let parts: Vec<_> = s.split('/').collect();\n    parts.len() == 2\n        && parts.iter().all(|p| {\n            !p.is_empty()\n                && p != \".\"\n                && p != \"..\"\n                && p.bytes().all(|b| b.is_ascii_alphanumeric() || b\"-_.\".contains(&b))\n        })\n}\nassert!(is_valid_repo(\"octocat/hello-world\"));","typeGuard":"fn as_owner_repo(value: &str) -> Option<(&str, &str)> {\n    let (owner, repo) = value.split_once('/')?;\n    let ok = |s: &str| {\n        !s.is_empty() && s != \".\" && s != \"..\"\n            && s.bytes().all(|b| b.is_ascii_alphanumeric() || b\"-_.\".contains(&b))\n    };\n    (ok(owner) && ok(repo)).then_some((owner, repo))\n}","tryCatchPattern":null,"preventionTips":["Always configure scopes as bare OWNER/REPO, never full URLs or SSH origins","Normalize (strip scheme, host, .git) before validating","Keep repo names to ASCII alphanumerics, `-`, `_`, `.`","Validate each path segment independently to catch empty or dotted segments early"],"tags":["github","validation","identifier-format","relay"],"backgroundTag":"invalid-identifier-format","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}