gitbutlerapp/gitbutler · error
ForgeNotAuthenticated
ForgeNotAuthenticated
Error message
No GitHub access token found for account '{account_id}'.\nRun 'but config forge auth' to re-authenticate. What it means
`GitHubClient::from_storage` resolves the account, then looks up its stored GitHub access token; when no token is stored for that account it returns this error, tagged with the `ForgeNotAuthenticated` context (`NOT_AUTHENTICATED`). It means the client cannot authenticate any request — credentials are missing, not invalid.
Solutions
- Run `but config forge auth` to authenticate the account with GitHub.
- Verify the correct account is selected/resolvable (`resolve_account`) and that it is the one with a stored token.
- In CI, provision a token via the setup step before invoking commands that need GitHub.
- If the token was removed by the OS keychain, re-authenticate to restore it.
Example fix
// before let client = GitHubClient::from_storage(Some(&account), storage)?; // panics/errors: no token // after // terminal: // $ but config forge auth let client = GitHubClient::from_storage(Some(&account), storage)?;
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check that the account has a stored token
let has_token = crate::token::get_gh_access_token(&account_id, storage)?.is_some();
if !has_token { eprintln!("Run 'but config forge auth' first"); } Try / catch
match GitHubClient::from_storage(Some(&account), storage) {
Ok(c) => c,
Err(e) if e.to_string().contains("No GitHub access token found") => {
return Err(anyhow!("GitHub not authenticated; run 'but config forge auth'"));
}
Err(e) => return Err(e),
} Prevention
- Run `but config forge auth` during setup and CI provisioning
- Fail fast with a friendly message when no token exists instead of deep call paths
- Re-authenticate after OS keychain clears or credential rotations
When it happens
Trigger: Constructing a `GitHubClient::from_storage` for an account that never completed `but config forge auth`, after tokens were cleared, or when the preferred account identifier names an account with no stored token.
Common situations: Fresh machine or clone without forge setup; running `but` in CI where the auth step was skipped; expired/cleared keychain entries; specifying the wrong account identifier.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- ForgeNotAuthenticated
- GitHub GraphQL request failed
- Known account is not correctly authenticated. Run
- No authenticated forge users found. Run
- No authenticated GitHub users found.\nRun 'but config forge…
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/9a7173b20ca2ebc0.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-github/src/client.rs:82
.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."
)
.context(NOT_AUTHENTICATED))
}
}
/// 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 58e5313667)