stablyai/orca · error · Error

Bitbucket request failed: no usable credential

Error message

Bitbucket request failed: no usable credential

What it means

Thrown by the Bitbucket requestJson helper when resolveBitbucketAuthConfig() yields no usable credential (hasAuth(config) is false) AND the caller passed throwOnFailure=true. The guard exists because issuing the request with no credential would return 404 on private repos, which the caller would misread as 'no pull request' and wrongly offer to create one.

Source

Thrown at src/main/bitbucket/client.ts:84

async function requestJson<T>(
  path: string,
  options: RequestOptions = {},
  // Why: the existing-review lookup behind Create must distinguish a real
  // transport/auth failure from an accepted "no PR". When true, a failed request
  // throws instead of collapsing to null so callers never report false not_found.
  throwOnFailure = false,
  // Why: a linked PR number can be stale (deleted PR, wrong repo). A 404 there
  // must fall through to the branch lookup rather than throw and hide the
  // branch's real review, so only that caller opts into it.
  notFoundIsNull = false
): Promise<T | null> {
  const config = resolveBitbucketAuthConfig()
  // Why: a denied keychain prompt leaves no usable credential. Issuing the
  // request anyway gets a 404 on private repos, which reads as "no pull
  // request" and offers Create for a branch that already has one.
  if (!hasAuth(config)) {
    if (throwOnFailure) {
      throw new Error('Bitbucket request failed: no usable credential')
    }
    return null
  }
  try {
    const response = await fetch(apiUrl(config.baseUrl, path, options.searchParams), {
      headers: {
        Accept: 'application/json',
        ...authHeaders(config)
      },
      signal: AbortSignal.timeout(options.timeoutMs ?? REQUEST_TIMEOUT_MS)
    })
    if (!response.ok) {
      await cancelUnreadResponseBody(response)
      if (response.status === 404 && notFoundIsNull) {
        return null
      }
      if (throwOnFailure) {
        throw new Error(`Bitbucket request failed: HTTP ${response.status}`)

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Check Bitbucket auth status (hasAuth / getStoredBitbucketCredentialError) before issuing throwOnFailure requests and prompt setup if missing.
  2. Catch the Error and direct the user to (re)authenticate rather than treating it as 'no PR'.
  3. Ensure the environment has keychain access for headless setups, or use env-based auth config.
  4. Re-prompt for the credential if getStoredBitbucketCredentialError indicates a denial.

Example fix

// before
const pr = await requestJson(path, {}, true)

// after
const config = resolveBitbucketAuthConfig()
if (!hasAuth(config)) {
  throw new Error('Bitbucket credential required — connect your account first')
}
const pr = await requestJson(path, {}, true)
Defensive patterns

Strategy: validation

Validate before calling

import { resolveBitbucketAuthConfig, hasAuth } from './bitbucket-auth-config'\n\nconst config = resolveBitbucketAuthConfig()\nif (!hasAuth(config)) {\n  throw new Error('Bitbucket credential required — connect your account first')\n}

Try / catch

try {\n  return await requestJson(path, options, true)\n} catch (err) {\n  if ((err as Error).message.includes('no usable credential')) {\n    await promptBitbucketReconnect()\n    return null\n  }\n  throw err\n}

Prevention

When it happens

Trigger: Calling a Bitbucket request with throwOnFailure=true when no credential is configured, the stored credential was deleted, or the OS keychain prompt was denied/cancelled leaving no usable token.

Common situations: User dismissed the keychain/password prompt, the stored app password expired or was revoked, Bitbucket credential setup was never completed, or a headless/SSH environment with no keychain access.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/82cfb2e9f7986328. Report an issue: GitHub.