{"record":{"id":"1d3ff91e710a3a1d","repo":"BerriAI/litellm","slug":"failed-to-fetch-file-file-path-via-json-endpoi","errorCode":null,"errorMessage":"Failed to fetch file '{file_path}' via JSON endpoint: {e}","messagePattern":"Failed to fetch file '(.+?)' via JSON endpoint: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/integrations/gitlab/gitlab_client.py","lineNumber":181,"sourceCode":"            content: Final = data.get(\"content\")\n            encoding: Final = data.get(\"encoding\", \"\")\n            if content and encoding == \"base64\":\n                try:\n                    return base64.b64decode(content).decode(\"utf-8\")\n                except Exception:\n                    return base64.b64decode(content).decode(\"utf-8\", errors=\"replace\")\n            return content\n        except Exception as e:\n            status: Final = getattr(getattr(e, \"response\", None), \"status_code\", None)\n            if status == 404:\n                return None\n            if status == 403:\n                raise Exception(\n                    f\"Access denied to file '{file_path}'. Check your GitLab permissions for project '{self.project}'.\"\n                )\n            if status == 401:\n                raise Exception(\"Authentication failed. Check your GitLab token and auth_method.\")\n            raise Exception(f\"Failed to fetch file '{file_path}' via JSON endpoint: {e}\")\n\n    def list_files(\n        self,\n        directory_path: str = \"\",\n        file_extension: str = \".prompt\",\n        recursive: bool = False,\n        *,\n        ref: str | None = None,\n    ) -> list[str]:\n        \"\"\"\n        List files in a directory with a specific extension using the repository tree API.\n\n        Args:\n            directory_path: Directory path in the repository (empty for repo root)\n            file_extension: File extension to filter by (default: .prompt)\n            recursive: If True, traverses subdirectories\n            ref: Optional override (tag/branch/SHA). Defaults to self.ref.\n","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/integrations/gitlab/gitlab_client.py#L163-L199","documentation":"Catch-all of the JSON fallback fetch: any non-401/403/404 failure while GETting /repository/files/<path>?ref=... is wrapped here with the original exception text appended. It is reached only after the raw endpoint returned 404, so infrastructure problems stack on top of whatever made the raw request miss.","triggerScenarios":"Self-hosted GitLab outage or restart; base_url missing /api/v4; TLS/proxy failures; 5xx or 429 responses; DNS resolution failure; timeouts fetching large prompt files through the JSON API.","commonSituations":"On-prem GitLab maintenance windows; misrouted private networks; rate-limited automation loops; slow mirrors adding latency to the base64 JSON path.","solutions":["Verify base_url ends with /api/v4 and the host is reachable","Reproduce with curl against the JSON endpoint (the URL printed in the error)","Retry transient 5xx/429/network errors with backoff","Cache prompt files locally to ride out GitLab blips"],"exampleFix":"# before\ncontent = client.get_file_content(\"prompts/greet.prompt\")\n\n# after: tolerate transient GitLab failures\nfor attempt in range(3):\n    try:\n        content = client.get_file_content(\"prompts/greet.prompt\")\n        break\n    except Exception as e:\n        if attempt == 2 or \"via JSON endpoint\" not in str(e):\n            raise\n        time.sleep(2 ** attempt)","handlingStrategy":"retry","validationCode":"import socket\nfrom urllib.parse import urlparse\n\n\ndef gitlab_host_reachable(base_url: str) -> bool:\n    host = urlparse(base_url).hostname or \"\"\n    try:\n        socket.getaddrinfo(host, 443)\n        return True\n    except socket.gaierror:\n        return False","typeGuard":null,"tryCatchPattern":"import time\n\n\ndef fetch_with_retry(client, path, attempts=3):\n    for i in range(attempts):\n        try:\n            return client.get_file_content(path)\n        except Exception as e:\n            msg = str(e)\n            retryable = \"via JSON endpoint\" in msg and any(\n                t in msg for t in (\"timed out\", \"ConnectError\", \"500\", \"502\", \"503\", \"429\")\n            )\n            if i == attempts - 1 or not retryable:\n                raise\n            time.sleep(2 ** i)","preventionTips":["Add DNS/reachability checks for the GitLab host to your readiness probe","Bound retries and cache prompt content locally","Monitor GitLab status pages for on-prem maintenance windows"],"tags":["gitlab","network","http","fallback"],"backgroundTag":"http-request-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}