gitbutlerapp/gitbutler · error · anyhow::Error

No GitHub access token found for account '{account_id}'. Run

Error message

No GitHub access token found for account '{account_id}'.
Run 'but config forge auth' to re-authenticate.

What it means

`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`.

Source

Thrown at crates/but-github/src/client.rs:80

            .default_headers(headers)
            .build()?;

        Ok(Self {
            client,
            base_url: GITHUB_API_BASE_URL.to_string(),
        })
    }

    /// Create a new instance of a GitHub client out of the stored accounts information.
    pub fn from_storage(
        storage: &but_forge_storage::Controller,
        preferred_account: Option<&crate::GithubAccountIdentifier>,
    ) -> anyhow::Result<Self> {
        let account_id = resolve_account(preferred_account, storage)?;
        if let Some(access_token) = crate::token::get_gh_access_token(&account_id, storage)? {
            account_id.client(&access_token)
        } else {
            Err(anyhow::anyhow!(
                "No GitHub access token found for account '{account_id}'.\nRun 'but config forge auth' to re-authenticate."
            ))
        }
    }

    /// Create a new instance of a GitHub client, with a custom base URL.
    ///
    /// This is used to create the GitHub client for Enterprise users.
    pub fn new_with_host_override(access_token: &Sensitive<String>, host: &str) -> Result<Self> {
        let mut headers = HeaderMap::new();
        headers.insert(
            USER_AGENT,
            HeaderValue::from_static("gb-github-integration"),
        );
        headers.insert(
            ACCEPT,
            HeaderValue::from_static("application/vnd.github+json"),
        );

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Run `but config forge auth` and complete GitHub authentication.
  2. 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.
  3. If the OS keyring was cleared or reset, re-authenticate to reseed the token.
  4. On headless Linux, make sure a secret service (gnome-keyring/KWallet) is running so tokens can persist.

Example fix

// before
let client = GithubClient::from_storage(&storage, preferred_account)?;

// after
let client = match GithubClient::from_storage(&storage, preferred_account) {
    Ok(client) => client,
    Err(e) if e.to_string().contains("No GitHub access token") => {
        prompt_reauth().await?; // equivalent of `but config forge auth`
        GithubClient::from_storage(&storage, preferred_account)?
    }
    Err(e) => return Err(e),
};
Defensive patterns

Strategy: try-catch

Validate before calling

use but_github::token::get_gh_access_token;

// Pre-check before building a client / firing a request
if get_gh_access_token(&account_id, &storage).ok().flatten().is_none() {
    // route the user to `but config forge auth` instead of failing mid-call
}

Try / catch

match but_github::GitHubClient::from_storage(&storage, preferred_account) {
    Ok(client) => client,
    Err(e) if e.to_string().contains("No GitHub access token") => {
        // start the re-auth flow; the error message already names the command
        anyhow::bail!("GitHub authentication required: {e}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/500ee522bc9454a8. Report an issue: GitHub.