{"record":{"id":"0adbe8c16c0d3ad0","repo":"BerriAI/litellm","slug":"authentication-failed-check-your-bitbucket-access","errorCode":null,"errorMessage":"Authentication failed. Check your BitBucket access token and permissions.","messagePattern":"Authentication failed\\. Check your BitBucket access token and permissions\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/integrations/bitbucket/bitbucket_client.py","lineNumber":114,"sourceCode":"                return response.text\n            else:\n                # For binary files or when content-type is not text, try to decode as base64\n                try:\n                    return base64.b64decode(response.content).decode(\"utf-8\")\n                except Exception:\n                    return response.text\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 None\n                elif e.response.status_code == 403:\n                    raise Exception(\n                        f\"Access denied to file '{file_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 fetch file '{file_path}': {e}\")\n            else:\n                raise Exception(f\"Error fetching file '{file_path}': {e}\")\n\n    def list_files(self, directory_path: str = \"\", file_extension: str = \".prompt\") -> list[str]:\n        \"\"\"\n        List files in a directory with a specific extension.\n\n        Args:\n            directory_path: Directory path in the repository (empty for root)\n            file_extension: File extension to filter by (default: .prompt)\n\n        Returns:\n            List of file paths\n        \"\"\"\n        safe_dir: Final = _sanitize_file_path(directory_path) if directory_path else \"\"\n        url: Final = f\"{self.base_url}/repositories/{self.workspace}/{self.repository}/src/{self.branch}/{safe_dir}\"","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/integrations/bitbucket/bitbucket_client.py#L96-L132","documentation":"get_file_content received HTTP 401 from the BitBucket src endpoint: the credential itself was rejected — expired or revoked app password/access token, a malformed Bearer header, or an auth-mode mismatch where app-password credentials are sent as a Bearer token (the client defaults to auth_method=\"token\"; basic auth with username is only used when configured).","triggerScenarios":"App password revoked when the owning account changed/rotated it; sending an app password with the default Bearer scheme; token pasted with whitespace/newlines from a secret file; workspace migrated to API tokens while the old credential type was deprecated.","commonSituations":"Silent expiry of bitbucket-tokens during long-running services; secrets rotated in vault but not reloaded by the process; copying a token for a different Atlassian product (e.g., a Jira API token).","solutions":["Regenerate the app password / access token and update the config or vault it came from","For app passwords set auth_method=\"basic\" and username so the client sends Basic auth instead of Bearer","Trim whitespace/newlines when loading the secret (e.g., .strip() on env reads)","Restart the process after credential rotation so the cached client headers are rebuilt"],"exampleFix":"// before\nBitBucketClient({\"workspace\": \"acme\", \"repository\": \"r\",\n                 \"access_token\": app_password})  # default Bearer -> 401\n// after\nBitBucketClient({\"workspace\": \"acme\", \"repository\": \"r\",\n                 \"access_token\": app_password,\n                 \"auth_method\": \"basic\", \"username\": \"svc-prompts\"})","handlingStrategy":"try-catch","validationCode":"import base64, os\n\ntoken = os.environ.get(\"BITBUCKET_APP_PASSWORD\", \"\").strip()\nif not token:\n    raise RuntimeError(\"BITBUCKET_APP_PASSWORD is empty or unset\")","typeGuard":"def looks_like_credential(secret: str) -> bool:\n    s = (secret or \"\").strip()\n    return len(s) >= 20 and not any(c.isspace() for c in s)","tryCatchPattern":"try:\n    content = client.get_file_content(path)\nexcept Exception as e:\n    if \"Authentication failed\" in str(e):\n        logger.error(\"bitbucket 401: credential expired/revoked or wrong auth_method\")\n        raise\n    raise","preventionTips":["Set auth_method=\"basic\" + username when using app passwords (default Bearer will 401)",".strip() secrets read from env/files — trailing newlines break auth","Rotate credentials in the vault and restart so headers rebuild","Distinguish 401 (credential bad) from 403 (credential fine, no permission) before debugging scopes"],"tags":["bitbucket","http-401","authentication","credentials","api"],"backgroundTag":"http-401-unauthorized","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}