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
- Grant repository read scope to the token/app password (Repositories -> Read)
- Add the authenticating account to the workspace or share the repo with it
- Authorize the token under workspace SSO in Atlassian account settings
- 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
- Grant Repositories:Read on tokens used for prompt discovery
- Remember 404 on listing returns [] — do not mistake silence for permission
- Probe listing once at startup to surface scope problems early
- Keep a fallback list of known prompt paths so one forbidden directory does not block boot
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Access denied to file
- access_denied
- Access denied to directory
- Access denied to file
- Access denied to prompt version
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)