BerriAI/litellm · error · Exception

Access denied to directory

Error message

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

What it means

list_files received HTTP 403 from GET /repositories/{workspace}/{repository}/src/{branch}/{directory}: authentication succeeded but the credential cannot read (list) that path — token lacks repository read scope, the account has no workspace membership, or SSO/IP restrictions apply. Note the asymmetry: 404 here returns [] (treated as an empty directory), so this error specifically means 'seen but forbidden'.

Solutions

  1. Grant repository read scope to the token/app password (Repositories -> Read)
  2. Add the authenticating account to the workspace or share the repo with it
  3. Authorize the token under workspace SSO in Atlassian account settings
  4. Confirm workspace/repository spelling — 403 commonly masks 'repo you cannot see'

Example fix

// before
client.list_files("prompts")  # token lacks repo read -> 403 wrapped
// after: recreate app password with Repositories:Read, then
client.list_files("prompts")  # -> ['prompts/triage.prompt', ...]
Defensive patterns

Strategy: try-catch

Validate before calling

# Cheap permission probe: an empty-directory listing exercises the same ACL
try:
    client.list_files("")
except Exception:
    raise

Try / catch

try:
    files = client.list_files("prompts")
except Exception as e:
    if "Access denied to directory" in str(e):
        logger.error("bitbucket 403 listing %s/%s: grant repository read to the token",
                     client.workspace, client.repository)
        raise
    raise

Prevention

When it happens

Trigger: Listing a private repo's directory with a snippet-only token; app password missing Repositories:Read; directory sits in a repo the service account was never granted; SSO-protected workspace with an unauthorized token; branch-level restriction on the listed branch.

Common situations: Service accounts provisioned for a different repo and reused for prompt libraries; new repos created private by default; token scopes trimmed during a security audit.

Understand the failure class

Related errors


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

Appendix: source

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

            data: Final = response.json()
            files: Final = []

            for item in data.get("values", []):
                if item.get("type") == "commit_file":
                    file_path = item.get("path", "")
                    if file_path.endswith(file_extension):
                        files.append(file_path)

            return files

        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 []
                elif e.response.status_code == 403:
                    raise Exception(
                        f"Access denied to directory '{directory_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 list files in '{directory_path}': {e}")
            else:
                raise Exception(f"Error listing files in '{directory_path}': {e}")

    def get_repository_info(self) -> dict[str, Any]:
        """
        Get information about the repository.

        Returns:
            Dictionary containing repository information
        """
        url: Final = f"{self.base_url}/repositories/{self.workspace}/{self.repository}"

View on GitHub (pinned to 77b7c6c40c)