{"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/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/integrations/bitbucket/bitbucket_client.py#L96-L132","documentation":"Raised by BitBucketClient.get_file when BitBucket returns 401 Unauthorized: the credentials themselves were rejected. Distinguished from 403 (error 335) — 401 means the token/app password is invalid, expired, or the Authorization header scheme does not match auth_method. The client sends Bearer tokens for auth_method 'token' and Basic for 'basic' with username.","triggerScenarios":"Expired or revoked app password; access_token containing whitespace/newline from copy-paste; auth_method 'basic' but username not set so Basic header never added (falls back to no/Bearer auth); using an OAuth token where an app password is expected.","commonSituations":"App passwords pasted with a trailing newline in env vars; token revoked when the owner changed their Bitbucket password; misconfigured auth_method mismatching the credential type; base64 of 'username:password' built from empty username.","solutions":["Regenerate the app password / token in Bitbucket and update the config","Strip whitespace: access_token.strip() when loading from env","For basic auth set both username and access_token (app password); for token auth use a valid API token with auth_method 'token'","Verify the credential with a direct curl to the BitBucket API before retrying through litellm"],"exampleFix":"# before\nconfig = {\"workspace\": \"w\", \"repository\": \"r\", \"access_token\": os.getenv(\"BB_TOKEN\"), \"auth_method\": \"basic\"}  # username missing -> 401\n\n# after\nconfig = {\n    \"workspace\": \"w\", \"repository\": \"r\",\n    \"access_token\": os.environ[\"BB_APP_PASSWORD\"].strip(),\n    \"auth_method\": \"basic\",\n    \"username\": \"my-user\",\n}","handlingStrategy":"try-catch","validationCode":"import httpx\n\ndef credentials_valid(workspace: str, token: str) -> bool:\n    r = httpx.get(\n        f\"https://api.bitbucket.org/2.0/repositories/{workspace}\",\n        auth=(\"x-token-auth\", token),\n        timeout=10,\n    )\n    return r.status_code == 200  # 401 means bad credentials","typeGuard":null,"tryCatchPattern":"try:\n    content = client.get_file(path)\nexcept Exception as e:\n    if \"Authentication failed\" in str(e):\n        raise CredentialsError(\"BitBucket token invalid/expired — regenerate it\") from e\n    raise","preventionTips":["Strip whitespace/newlines from tokens copied from vaults or env","Set auth_method 'basic' with username when using app passwords","Rotate credentials on a schedule; Bitbucket revokes app passwords on password change","Validate credentials once at startup with a cheap API call"],"tags":["bitbucket","http-401","authentication","credentials"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}