{"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/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/integrations/bitbucket/bitbucket_client.py#L98-L134","documentation":"get_file_content wraps any HTTP status other than 401/403/404 into this generic Exception together with the underlying error text. Typical causes are 429 rate limiting (BitBucket Cloud throttles unauthenticated-heavy or bursty traffic), 5xx outages, and 400-class responses from a malformed branch or path. The status code itself is only visible inside the appended {e} text.","triggerScenarios":"Burst-fetching many .prompt files at startup tripping the rate limit; BitBucket Cloud incident returning 5xx; a branch name with characters that break the URL; paginated endpoints mid-outage.","commonSituations":"Fan-out prompt loading in CI where dozens of clients start simultaneously; retry storms amplifying a transient 502; branch renamed after config was written.","solutions":["Inspect the appended error text for the status code: 429 -> back off (honor Retry-After header) and reduce concurrency/enable caching","5xx -> check status.bitbucket.org and retry with exponential backoff","400-ish -> verify branch name and path, and re-run _sanitize_file_path rules mentally (no '#', '?', '..')","Cache fetched prompts so transient failures do not cascade at startup"],"exampleFix":"// before\ncontent = client.get_file_content(path)  # burst of these -> 429 wrapped in 295\n// after\nimport time\nfor attempt in range(5):\n    try:\n        content = client.get_file_content(path)\n        break\n    except Exception as e:\n        if \"429\" in str(e) and attempt < 4:\n            time.sleep(2 ** attempt)\n            continue\n        raise","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"import random, time\n\nfor attempt in range(5):\n    try:\n        content = client.get_file_content(path)\n        break\n    except Exception as e:\n        msg = str(e)\n        transient = (\"429\" in msg) or (\"5\" in msg.split(\" \")[-1][:1] and \"Failed to fetch\" in msg)\n        if \"Failed to fetch file\" in msg and transient and attempt < 4:\n            time.sleep((2 ** attempt) + random.random())\n            continue\n        raise","preventionTips":["Cache prompt file contents; only re-fetch on explicit invalidation","Jitter and cap concurrency when loading many files at boot","Honor Retry-After when a 429 is visible in the wrapped text","Check status.bitbucket.org before paging anyone for 5xx"],"tags":["bitbucket","http-error","rate-limit","api","transient"],"backgroundTag":"http-request-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}