{"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":"validation","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/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/github_relay.rs#L122-L158","documentation":"The `repository` function in the GitHub relay normalizes and validates repository identifiers. Each OWNER and REPO segment must be non-empty, not `.` or `..`, and contain only ASCII alphanumerics, `-`, `_`, or `.`; anything else (or a missing `/` separator) yields this error. The value is lowercased on success so scope checks are case-insensitive.","triggerScenarios":"Calling `repository` (via `from_flags`, `expand_repository`, or `authorize`) with a value missing the `/` separator (`myrepo`), containing invalid characters (`owner/repo!`, `owner name/repo`), empty segments (`/repo`, `owner/`), or `.`/`..` segments.","commonSituations":"Typing a full GitHub URL instead of OWNER/REPO; including a branch or path suffix (`owner/repo/subdir`); shell quoting or whitespace inside the value; passing a shorthand like `~` or a local path.","solutions":["Pass the repository exactly as `OWNER/REPO` with a single `/`, e.g. `octocat/hello-world`.","Strip scheme, host, `.git` suffix where not supported, and any trailing path or ref before calling.","Remove invalid characters (spaces, `:`, `@`, unicode) from owner and repo names."],"exampleFix":"// before\nlet scope = relay::from_flags(\"https://github.com/octocat/hello-world\")?;\n// after\nlet scope = relay::from_flags(\"octocat/hello-world\")?;","handlingStrategy":"validation","validationCode":"fn is_owner_repo(s: &str) -> bool {\n    let (o, r) = match s.split_once('/') { Some(x) => x, None => return false };\n    let ok = |s: &str| !s.is_empty() && s != \".\" && s != \"..\"\n        && s.bytes().all(|b| b.is_ascii_alphanumeric() || b\"-_..contains(&b));\n    ok(o) && ok(r)\n}\nassert!(is_owner_repo(\"octocat/hello-world\"));","typeGuard":"fn as_owner_repo(v: &str) -> Option<String> {\n    let (o, r) = v.split_once('/')?;\n    let ok = |s: &str| !s.is_empty() && !matches!(s, \".\" | \"..\")\n        && s.bytes().all(|b| b.is_ascii_alphanumeric() || b\"-_..contains(&b));\n    (ok(o) && ok(r)).then(|| v.to_ascii_lowercase())\n}","tryCatchPattern":"match relay::from_flags(input) {\n    Err(e) if e.to_string().contains(\"OWNER/REPO\") => eprintln!(\"pass owner/repo, got {input:?}\"),\n    Err(e) => return Err(e),\n    Ok(scope) => scope,\n}","preventionTips":["Normalize GitHub URLs to owner/repo before passing (strip scheme/host and trailing paths).","Keep a small parser helper for repo identifiers and use it everywhere.","Never pass URLs, branch refs, or local paths where OWNER/REPO is expected."],"tags":["validation","github","identifier","format"],"backgroundTag":"invalid-identifier-format","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}