gitbutlerapp/gitbutler · error

NOT_AUTHENTICATED

NOT_AUTHENTICATED

Error message

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

What it means

`BitbucketClient::from_storage` builds an authenticated client for a Bitbucket account. If no OAuth access token is stored for the resolved account, it returns this error with the NOT_AUTHENTICATED code, instructing the user to re-authenticate via `but config forge auth`.

Solutions

  1. Run `but config forge auth` to re-authenticate with Bitbucket
  2. Verify the account ID is correct if a preferred account was specified
  3. Check stored forge credentials/token storage for corruption or a cleared keychain
  4. Re-add the Bitbucket account to the forges configuration
Defensive patterns

Strategy: try-catch

Validate before calling

// check a token exists before constructing the client
let has_token = but_bitbucket::token::get_bb_access_token(&account_id, storage)?
    .is_some();
if !has_token {
    eprintln!("run 'but config forge auth' first");
}

Try / catch

match BitbucketClient::from_storage(storage, preferred) {
    Err(e) if e.downcast_ref::<api_error>() .map(|c| c.code == "NOT_AUTHENTICATED").unwrap_or(false)
        || format!("{e:#}").contains("No Bitbucket access token") =>
    {
        prompt_forge_auth();
        Err(e)
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling any Bitbucket client construction (`from_storage`) for an account whose token was never stored, was deleted, or was revoked/invalidated in storage.

Common situations: Token revoked on Bitbucket's side; `but config forge auth` never run for this account; local credentials storage cleared or migrated; using a preferred account ID that exists but has no token.

Understand the failure class

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/b8a69208c32ceb77. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-bitbucket/src/client.rs:64

            .default_headers(headers)
            .timeout(BITBUCKET_REQUEST_TIMEOUT)
            .build()?;

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

    pub fn from_storage(
        storage: &but_forge_storage::Controller,
        preferred_account: Option<&crate::BitbucketAccountIdentifier>,
    ) -> Result<Self> {
        let account_id = resolve_account(preferred_account, storage)?;
        if let Some(access_token) = crate::token::get_bb_access_token(&account_id, storage)? {
            account_id.client(&access_token)
        } else {
            Err(anyhow::anyhow!(
                "No Bitbucket access token found for account '{account_id}'.\nRun 'but config forge auth' to re-authenticate."
            )
            .context(NOT_AUTHENTICATED))
        }
    }

    pub async fn get_authenticated(&self) -> Result<AuthenticatedUser> {
        let url = format!("{}/user", self.base_url);
        let response = self.client.get(&url).send().await?;

        if !response.status().is_success() {
            return Err(HttpStatusError {
                status: response.status(),
            }
            .into());
        }

        let user: BitbucketApiUser = response.json().await?;

View on GitHub (pinned to 58e5313667)