BerriAI/litellm · error · Exception

Access denied to file

Error message

Access denied to file '{file_path}'. Check your BitBucket permissions for workspace '{self.workspace}' and repository '{self.repository}'.

What it means

get_file_content received HTTP 403 from GET /repositories/{workspace}/{repository}/src/{branch}/{path}: authentication succeeded (otherwise 401) but the credential is not permitted to read this resource — the token/app password lacks repository read scope, the account is not a member of the workspace, or workspace SSO/IP restrictions block it. The original HTTP details are discarded and only this generic message is raised.

Solutions

  1. Recreate the token/app password granting repository read scope (app passwords: tick Repositories -> Read)
  2. Verify the authenticating account has at least read access to that workspace/repo (workspace -> Members / repo -> Permissions)
  3. If the workspace requires SSO, approve/authorize the token for that workspace in Atlassian account settings
  4. Double-check workspace and repository spelling — BitBucket can return 403 rather than 404 for repos hidden from your identity

Example fix

// before: token without repo read scope
BitBucketClient({"workspace": "acme", "repository": "prompt-library", "access_token": narrow_token})
// after: app password with Repositories:Read, basic auth
BitBucketClient({"workspace": "acme", "repository": "prompt-library",
                 "access_token": os.environ["BITBUCKET_APP_PASSWORD"],
                 "auth_method": "basic", "username": "svc-prompts"})
Defensive patterns

Strategy: try-catch

Validate before calling

# No API to pre-check scopes without a request; verify access cheaply:
probe = client.list_files("")  # or get_file_content("README.md")
# 403 on the probe means the token cannot read this repo at all

Try / catch

try:
    content = client.get_file_content("prompts/triage.prompt")
except Exception as e:
    if "Access denied" in str(e):
        logger.error("bitbucket 403: token lacks repository read scope for %s/%s",
                     client.workspace, client.repository)
        raise
    raise

Prevention

When it happens

Trigger: API token with only account/snippet scopes but not repository:read; app password created without the 'Repositories: Read' permission; repository private to another team in the same workspace; SSO-protected workspace where the token was never authorized; branch-level restrictions on the requested branch.

Common situations: Rotating to a least-privilege token that turned out too narrow; org tokens vs personal tokens confusion; on-prem-style SSO enforcement enabled after the token was created; new repos defaulting to private.

Understand the failure class

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/b1de80c3c88a7cd3. Report an issue: GitHub.

Appendix: source

Thrown at litellm/integrations/bitbucket/bitbucket_client.py:110

            response.raise_for_status()

            # BitBucket returns file content as base64 encoded
            if response.headers.get("content-type", "").startswith("text/"):
                return response.text
            else:
                # For binary files or when content-type is not text, try to decode as base64
                try:
                    return base64.b64decode(response.content).decode("utf-8")
                except Exception:
                    return response.text

        except Exception as e:
            # Check if it's an HTTP error
            if hasattr(e, "response") and hasattr(e.response, "status_code"):
                if e.response.status_code == 404:
                    return None
                elif e.response.status_code == 403:
                    raise Exception(
                        f"Access denied to file '{file_path}'. Check your BitBucket permissions for workspace '{self.workspace}' and repository '{self.repository}'."
                    )
                elif e.response.status_code == 401:
                    raise Exception("Authentication failed. Check your BitBucket access token and permissions.")
                else:
                    raise Exception(f"Failed to fetch file '{file_path}': {e}")
            else:
                raise Exception(f"Error fetching file '{file_path}': {e}")

    def list_files(self, directory_path: str = "", file_extension: str = ".prompt") -> list[str]:
        """
        List files in a directory with a specific extension.

        Args:
            directory_path: Directory path in the repository (empty for root)
            file_extension: File extension to filter by (default: .prompt)

        Returns:

View on GitHub (pinned to 77b7c6c40c)