{"record":{"id":"500ee522bc9454a8","repo":"gitbutlerapp/gitbutler","slug":"no-github-access-token-found-for-account-account","errorCode":null,"errorMessage":"No GitHub access token found for account '{account_id}'.\nRun 'but config forge auth' to re-authenticate.","messagePattern":"No GitHub access token found for account '(.+?)'\\.\nRun 'but config forge auth' to re-authenticate\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/but-github/src/client.rs","lineNumber":80,"sourceCode":"            .default_headers(headers)\n            .build()?;\n\n        Ok(Self {\n            client,\n            base_url: GITHUB_API_BASE_URL.to_string(),\n        })\n    }\n\n    /// Create a new instance of a GitHub client out of the stored accounts information.\n    pub fn from_storage(\n        storage: &but_forge_storage::Controller,\n        preferred_account: Option<&crate::GithubAccountIdentifier>,\n    ) -> anyhow::Result<Self> {\n        let account_id = resolve_account(preferred_account, storage)?;\n        if let Some(access_token) = crate::token::get_gh_access_token(&account_id, storage)? {\n            account_id.client(&access_token)\n        } else {\n            Err(anyhow::anyhow!(\n                \"No GitHub access token found for account '{account_id}'.\\nRun 'but config forge auth' to re-authenticate.\"\n            ))\n        }\n    }\n\n    /// Create a new instance of a GitHub client, with a custom base URL.\n    ///\n    /// This is used to create the GitHub client for Enterprise users.\n    pub fn new_with_host_override(access_token: &Sensitive<String>, host: &str) -> Result<Self> {\n        let mut headers = HeaderMap::new();\n        headers.insert(\n            USER_AGENT,\n            HeaderValue::from_static(\"gb-github-integration\"),\n        );\n        headers.insert(\n            ACCEPT,\n            HeaderValue::from_static(\"application/vnd.github+json\"),\n        );","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/caf1f223d3cfb94488c9198ad34487c6006c648f/crates/but-github/src/client.rs#L62-L98","documentation":"`GithubClient::from_storage` resolves an account (the preferred identifier if given, else the stored default) and looks up its access token in OS secret storage via `but_github::token::get_gh_access_token`. If that lookup returns `None`, no HTTP client can be built and this error is returned — the message itself carries the remediation command `but config forge auth`.","triggerScenarios":"Any but-github call that constructs a client from storage while no token exists for the resolved `account_id`: after logout, on a fresh machine, when the stored token was deleted, when the token expired and was purged, or when a `preferred_account` identifier points at an account that never completed authentication.","commonSituations":"Fresh installs; OS keyring wiped, locked, or unavailable (headless Linux without a secret-service daemon); tokens revoked at github.com/settings; switching accounts via a preferred forge user; CI machines with no persistent secret store.","solutions":["Run `but config forge auth` and complete GitHub authentication.","Verify which account is being resolved — a `preferred_account` id that was never logged in yields this error even when other accounts have valid tokens.","If the OS keyring was cleared or reset, re-authenticate to reseed the token.","On headless Linux, make sure a secret service (gnome-keyring/KWallet) is running so tokens can persist."],"exampleFix":"// before\nlet client = GithubClient::from_storage(&storage, preferred_account)?;\n\n// after\nlet client = match GithubClient::from_storage(&storage, preferred_account) {\n    Ok(client) => client,\n    Err(e) if e.to_string().contains(\"No GitHub access token\") => {\n        prompt_reauth().await?; // equivalent of `but config forge auth`\n        GithubClient::from_storage(&storage, preferred_account)?\n    }\n    Err(e) => return Err(e),\n};","handlingStrategy":"try-catch","validationCode":"use but_github::token::get_gh_access_token;\n\n// Pre-check before building a client / firing a request\nif get_gh_access_token(&account_id, &storage).ok().flatten().is_none() {\n    // route the user to `but config forge auth` instead of failing mid-call\n}","typeGuard":null,"tryCatchPattern":"match but_github::GitHubClient::from_storage(&storage, preferred_account) {\n    Ok(client) => client,\n    Err(e) if e.to_string().contains(\"No GitHub access token\") => {\n        // start the re-auth flow; the error message already names the command\n        anyhow::bail!(\"GitHub authentication required: {e}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Authenticate at app start (and after any 401 from GitHub) instead of lazily mid-action.","Treat a missing token like an expired credential: prompt re-auth, never continue unauthenticated.","On headless Linux, ensure a secret service (gnome-keyring/KWallet) is available or tokens cannot persist.","Validate that a preferred account identifier refers to an account that actually completed auth."],"tags":["rust","but-github","authentication","access-token","keyring","github"],"backgroundTag":"missing-access-token","analyzedSha":"caf1f223d3cfb94488c9198ad34487c6006c648f","analyzedAt":"2026-08-20T07:55:40.983Z","schemaVersion":2},"datasetVersion":"2026-09-01T03:17:15.561Z"}