{"record":{"id":"f47200b532b56190","repo":"BerriAI/litellm","slug":"access-denied-to-directory-directory-path-che","errorCode":null,"errorMessage":"Access denied to directory '{directory_path}'. Check your BitBucket permissions for workspace '{self.workspace}' and repository '{self.repository}'.","messagePattern":"Access denied to directory '(.+?)'\\. Check your BitBucket permissions for workspace '(.+?)' and repository '(.+?)'\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/integrations/bitbucket/bitbucket_client.py","lineNumber":155,"sourceCode":"\n            data: Final = response.json()\n            files: Final = []\n\n            for item in data.get(\"values\", []):\n                if item.get(\"type\") == \"commit_file\":\n                    file_path = item.get(\"path\", \"\")\n                    if file_path.endswith(file_extension):\n                        files.append(file_path)\n\n            return files\n\n        except Exception as e:\n            # Check if it's an HTTP error\n            if hasattr(e, \"response\") and hasattr(e.response, \"status_code\"):\n                if e.response.status_code == 404:\n                    return []\n                elif e.response.status_code == 403:\n                    raise Exception(\n                        f\"Access denied to directory '{directory_path}'. Check your BitBucket permissions for workspace '{self.workspace}' and repository '{self.repository}'.\"\n                    )\n                elif e.response.status_code == 401:\n                    raise Exception(\"Authentication failed. Check your BitBucket access token and permissions.\")\n                else:\n                    raise Exception(f\"Failed to list files in '{directory_path}': {e}\")\n            else:\n                raise Exception(f\"Error listing files in '{directory_path}': {e}\")\n\n    def get_repository_info(self) -> dict[str, Any]:\n        \"\"\"\n        Get information about the repository.\n\n        Returns:\n            Dictionary containing repository information\n        \"\"\"\n        url: Final = f\"{self.base_url}/repositories/{self.workspace}/{self.repository}\"\n","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/integrations/bitbucket/bitbucket_client.py#L137-L173","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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'"],"exampleFix":"// before\nclient.list_files(\"prompts\")  # token lacks repo read -> 403 wrapped\n// after: recreate app password with Repositories:Read, then\nclient.list_files(\"prompts\")  # -> ['prompts/triage.prompt', ...]","handlingStrategy":"try-catch","validationCode":"# Cheap permission probe: an empty-directory listing exercises the same ACL\ntry:\n    client.list_files(\"\")\nexcept Exception:\n    raise","typeGuard":null,"tryCatchPattern":"try:\n    files = client.list_files(\"prompts\")\nexcept Exception as e:\n    if \"Access denied to directory\" in str(e):\n        logger.error(\"bitbucket 403 listing %s/%s: grant repository read to the token\",\n                     client.workspace, client.repository)\n        raise\n    raise","preventionTips":["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"],"tags":["bitbucket","http-403","permissions","directory-listing","api"],"backgroundTag":"http-403-forbidden","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}