{"record":{"id":"3fc930df1106e2a3","repo":"nikivdev/code","slug":"gh-auth-token-returned-empty-token","errorCode":null,"errorMessage":"`gh auth token` returned empty token","messagePattern":"`gh auth token` returned empty token","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/pr_edit.rs","lineNumber":405,"sourceCode":"        entry.public.state = SyncState::Error;\n        entry.public.last_error = Some(err);\n    }\n\n    async fn get_gh_token(&self) -> Result<String> {\n        if let Some(t) = self.gh_token.read().await.clone() {\n            return Ok(t);\n        }\n\n        let out = Command::new(\"gh\")\n            .args([\"auth\", \"token\"])\n            .output()\n            .context(\"failed to run `gh auth token`\")?;\n        if !out.status.success() {\n            bail!(\"`gh auth token` failed; run `gh auth login`\");\n        }\n        let token = String::from_utf8_lossy(&out.stdout).trim().to_string();\n        if token.is_empty() {\n            bail!(\"`gh auth token` returned empty token\");\n        }\n\n        *self.gh_token.write().await = Some(token.clone());\n        Ok(token)\n    }\n\n    async fn write_status_json(&self) -> Result<()> {\n        let snapshot = self.status_snapshot().await;\n        let json = serde_json::to_string_pretty(&snapshot)?;\n\n        let tmp = self.dir.join(format!(\".{STATUS_FILENAME}.tmp\"));\n        let out = self.dir.join(STATUS_FILENAME);\n        std::fs::write(&tmp, json)?;\n        // Best-effort atomic replace.\n        let _ = std::fs::rename(&tmp, &out);\n        Ok(())\n    }\n","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/pr_edit.rs#L387-L423","documentation":"After `gh auth token` exits successfully, get_gh_token trims stdout and rejects an empty result. A zero exit with no token means gh's stored credential state is inconsistent, and using an empty token would produce confusing upstream auth failures.","triggerScenarios":"gh exits 0 but prints nothing to stdout — e.g. a hosts.yml entry exists without a stored token, or an old/patched gh version emits the token to a different stream.","commonSituations":"Corrupt gh credential store after partial logout; gh version mismatch where `gh auth token` prints to stderr; wrapper scripts around gh that swallow output.","solutions":["Re-authenticate: `gh auth login` (or `gh auth login --with-token < token.txt`).","Check `gh auth token` output directly and inspect `gh auth status` for inconsistencies.","Ensure you are using an official, current gh binary — not a wrapper that prints nothing."],"exampleFix":"// before\n$ gh auth token\n(no output, exit 0)\n$ f pr-edit ...\n// `gh auth token` returned empty token\n\n// after\n$ gh auth login\n$ gh auth token\nghp_xxxx\n$ f pr-edit ...","handlingStrategy":"try-catch","validationCode":"let out = std::process::Command::new(\"gh\")\n    .args([\"auth\", \"token\"])\n    .output()?;\nlet token = String::from_utf8_lossy(&out.stdout).trim().to_string();\nif !out.status.success() || token.is_empty() {\n    eprintln!(\"gh returned no usable token; re-run `gh auth login`\");\n}","typeGuard":null,"tryCatchPattern":"match pr_edit_result {\n    Err(e) if e.to_string().contains(\"empty token\") => {\n        eprintln!(\"gh credential store is inconsistent; run `gh auth login` again\");\n    }\n    other => other?,\n}","preventionTips":["Re-login after partial `gh auth logout` to keep hosts.yml consistent.","Verify `gh auth token` prints a non-empty value before relying on it.","Avoid wrapper scripts around gh that may suppress stdout."],"tags":["github","auth","token","gh"],"backgroundTag":"empty-auth-token","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}