{"record":{"id":"43ad96301b659866","repo":"nikivdev/code","slug":"gh-login-was-empty","errorCode":null,"errorMessage":"gh login was empty","messagePattern":"gh login was empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/repos.rs","lineNumber":1477,"sourceCode":"\nfn normalize_git_url(url: &str) -> String {\n    url.trim()\n        .trim_end_matches('/')\n        .trim_end_matches(\".git\")\n        .to_string()\n}\n\nfn github_login() -> Result<String> {\n    let output = Command::new(\"gh\")\n        .args([\"api\", \"user\", \"-q\", \".login\"])\n        .output()\n        .context(\"failed to run gh api user\")?;\n    if !output.status.success() {\n        bail!(\"gh api user failed\");\n    }\n    let login = String::from_utf8_lossy(&output.stdout).trim().to_string();\n    if login.is_empty() {\n        bail!(\"gh login was empty\");\n    }\n    Ok(login)\n}\n\nfn resolve_remote_default_branch_in(repo_root: &Path, remote: &str) -> Option<String> {\n    let head_ref = format!(\"refs/remotes/{remote}/HEAD\");\n    if let Ok(symbolic) = git_capture_in(repo_root, &[\"symbolic-ref\", &head_ref]) {\n        let prefix = format!(\"refs/remotes/{remote}/\");\n        if let Some(branch) = symbolic.trim().strip_prefix(&prefix)\n            && !branch.is_empty()\n        {\n            return Some(branch.to_string());\n        }\n    }\n\n    for candidate in [\"main\", \"master\", \"dev\", \"trunk\"] {\n        if git_ref_exists_in(repo_root, &format!(\"refs/remotes/{remote}/{candidate}\")) {\n            return Some(candidate.to_string());","sourceCodeStart":1459,"sourceCodeEnd":1495,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/repos.rs#L1459-L1495","documentation":"After a successful `gh api user` call, the trimmed stdout login is checked; if gh reports success but returns an empty login string, this bail fires. It guards against authenticated-but-anonymous or malformed responses.","triggerScenarios":"`gh api user` exits 0 but `.login` is empty/whitespace — e.g. unusual token types (fine-grained/OAuth app tokens) or mocked/filtered gh wrappers returning empty output.","commonSituations":"Corporate gh wrappers/proxies stripping output; token without permission to read the user profile; piping gh output through filters that drop it.","solutions":["Run `gh api user -q .login` manually to confirm what it prints.","Re-authenticate with `gh auth login` using a user token (PAT/classic OAuth).","Check for wrapper scripts/aliases intercepting `gh` and remove or fix them."],"exampleFix":"// before: empty login from wrapper\n$ gh() { echo -n \"\"; }   # broken wrapper\n// after: ensure real gh resolves the login\n$ gh api user -q .login   # prints e.g. octocat","handlingStrategy":"validation","validationCode":"fn gh_login_resolves() -> bool {\n    std::process::Command::new(\"gh\")\n        .args([\"api\", \"user\", \"-q\", \".login\"])\n        .output()\n        .map(|o| o.status.success() && !o.stdout.iter().all(|&b| b.is_ascii_whitespace()))\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match github_login() {\n    Err(e) if e.to_string().contains(\"gh login was empty\") => {\n        // check for wrappers intercepting gh, or tokens lacking user:read\n        eprintln!(\"gh returned empty login; run: gh auth login\");\n    }\n    r => r?,\n}","preventionTips":["Ensure `gh` resolves to the real binary (`which gh`), not a wrapper function.","Use a token with read access to the user profile (classic PAT or OAuth).","Verify with `gh api user -q .login` before relying on login resolution.","Remove shell aliases/functions that override `gh` in automation shells."],"tags":["github","cli","authentication","gh"],"backgroundTag":"gh-cli-auth-failed","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}