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
- 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.
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
- 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.
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- No GitLab access token found for account '{account_id}'. Run
- unused
- Could not restore all review targets after the push failed:
- GitHub GraphQL request failed: {}
- No authenticated GitHub users found.\nRun 'but config forge
AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20).
Data as JSON: /api/errors/500ee522bc9454a8.
Report an issue: GitHub.