{"record":{"id":"aca10bff8d6b83dc","repo":"BerriAI/litellm","slug":"failed-to-fetch-file-file-path-e","errorCode":null,"errorMessage":"Failed to fetch file '{file_path}': {e}","messagePattern":"Failed to fetch file '(.+?)': (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/integrations/bitbucket/bitbucket_client.py","lineNumber":116,"sourceCode":"                # 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}\"\n\n        try:","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/integrations/bitbucket/bitbucket_client.py#L98-L134","documentation":"Raised by BitBucketClient.get_file when the BitBucket API returns an HTTP error status other than 404/403/401 (those have dedicated handling). The original exception is embedded in the message text. Typical causes are 400 (bad request from malformed path/params), 429 (rate limit), or 5xx (BitBucket outage). The underlying status code is only visible inside '{e}'.","triggerScenarios":"Rate limiting (429) during heavy prompt loading; BitBucket API 5xx incidents; 400 from malformed request paths; network timeouts surfacing as HTTP errors; unexpected redirects.","commonSituations":"Fetching many .prompt files in a loop (list_files + get_file) without backoff; BitBucket cloud maintenance windows; a path that survives sanitization but still produces a bad request.","solutions":["Inspect the embedded exception text in the message for the actual status code","Add rate-limit handling/backoff around get_file; reduce parallel fetches","If 5xx, retry after a delay or check https://status.atlassian.com for BitBucket incidents","If 400, log the exact URL/path used and compare with a working curl request"],"exampleFix":"# before\nfor p in client.list_files():\n    client.get_file(p)  # burst of requests -> 429 wrapped in generic error\n\n# after\nimport time\nfor p in client.list_files():\n    for attempt in range(3):\n        try:\n            client.get_file(p)\n            break\n        except Exception as e:\n            if \"429\" in str(e) and attempt < 2:\n                time.sleep(2 ** attempt)\n            else:\n                raise","handlingStrategy":"retry","validationCode":"import httpx\n\ndef api_healthy(token: str) -> bool:\n    try:\n        r = httpx.get(\"https://api.bitbucket.org/2.0/hooks\", auth=(\"x-token-auth\", token), timeout=5)\n        return r.status_code < 500\n    except httpx.HTTPError:\n        return False","typeGuard":null,"tryCatchPattern":"import time\n\ndef get_file_with_retry(client, path: str, attempts: int = 3):\n    for i in range(attempts):\n        try:\n            return client.get_file(path)\n        except Exception as e:\n            transient = any(s in str(e) for s in (\"429\", \"500\", \"502\", \"503\", \"timeout\"))\n            if transient and i < attempts - 1:\n                time.sleep(2 ** i)\n                continue\n            raise","preventionTips":["Cache fetched .prompt files; they rarely change, so avoid per-request fetches","Add exponential backoff with jitter around get_file calls","Watch the embedded status code in the message to distinguish rate limits from 400s","Batch list_files+get_file operations instead of tight loops"],"tags":["bitbucket","http-error","rate-limit","exception-wrapping"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}