{"record":{"id":"612ff51723deee1e","repo":"BerriAI/litellm","slug":"access-denied-to-file-file-path-check-your-gi","errorCode":null,"errorMessage":"Access denied to file '{file_path}'. Check your GitLab permissions for project '{self.project}'.","messagePattern":"Access denied to file '(.+?)'\\. Check your GitLab permissions for project '(.+?)'\\.","errorType":"exception","errorClass":"Exception","httpStatus":403,"severity":"error","filePath":"litellm/integrations/gitlab/gitlab_client.py","lineNumber":145,"sourceCode":"            if resp.status_code == 404:\n                # Fallback to JSON endpoint\n                return self._get_file_content_via_json(file_path, ref=ref)\n            resp.raise_for_status()\n\n            ctype: Final = (resp.headers.get(\"content-type\") or \"\").lower()\n            if ctype.startswith(\"text/\") or \"charset=\" in ctype or ctype.startswith(\"application/json\"):\n                return resp.text\n            try:\n                return resp.content.decode(\"utf-8\")\n            except Exception:\n                return resp.content.decode(\"utf-8\", errors=\"replace\")\n\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}': {e}\")\n\n    def _get_file_content_via_json(self, file_path: str, *, ref: str | None = None) -> str | None:\n        \"\"\"\n        Fallback for get_file_content(): use the JSON file API which returns base64 content.\n        \"\"\"\n        json_url: Final = self._file_json_url(file_path, ref=ref)\n        try:\n            resp: Final = self.http_handler.get(json_url, headers=self.headers)\n            if resp.status_code == 404:\n                return None\n            resp.raise_for_status()\n            data: Final = resp.json()\n            content: Final = data.get(\"content\")","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/integrations/gitlab/gitlab_client.py#L127-L163","documentation":"Raised by GitLabClient._get_file_content_via_raw when the GitLab raw-file API returns HTTP 403: the token is valid and authenticated, but the authenticated identity lacks permission to read the repository/file — e.g. a project without Guest+ access, a private repo with an outsider token, or IP restrictions/robot bans on the raw endpoint. The status is extracted from the httpx exception's .response.status_code.","triggerScenarios":"get_file_content() on a private project with a token whose owner is not a member; using a group access token scoped to a different group; GitLab rate-limiting or blocking the raw endpoint (raw endpoints can be disabled by admins); requesting a file in a protected branch the identity cannot read.","commonSituations":"Shared CI tokens that work for one group but not another; personal access tokens with scopes like api only vs read_api on restricted projects; self-managed GitLab instances where the raw endpoint is behind additional ACLs; prompt repos recently switched to private without re-issuing tokens.","solutions":["Verify the token's identity has at least Reporter read access to the project: curl -H 'Private-Token: <token>' 'https://gitlab.com/api/v4/projects/<id>/repository/files/<path>?ref=main'.","If using auth_method oauth, confirm the OAuth token has read_repository scope and Bearer auth is intended.","Ask the admin to enable the raw endpoint or stop blocking it for API clients if the 403 comes from endpoint restrictions.","Point to a project the token can access or mint a project access token for that repo."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import httpx, urllib.parse\n\ndef can_read_project_file(base_url: str, project: str, file_path: str, ref: str, token: str) -> None:\n    enc = urllib.parse.quote(project, safe=\"\")\n    fpath = urllib.parse.quote(file_path, safe=\"\")\n    resp = httpx.get(\n        f\"{base_url}/projects/{enc}/repository/files/{fpath}/raw\",\n        params={\"ref\": ref},\n        headers={\"Private-Token\": token},\n        timeout=10,\n    )\n    if resp.status_code == 403:\n        raise PermissionError(\"Token cannot read this project/file — grant Reporter+ access\")\n    resp.raise_for_status()","typeGuard":null,"tryCatchPattern":"try:\n    content = client.get_file_content(\"chat/greet.prompt\")\nexcept Exception as e:\n    if \"Access denied\" in str(e):\n        raise PermissionError(f\"GitLab token lacks read access: {e}\") from e\n    raise","preventionTips":["Run an access smoke-test per project at deploy time using the same token.","Prefer project access tokens scoped to the prompt repo over broad personal tokens.","Track token scopes and project membership in your secrets-management runbook."],"tags":["gitlab","http-403","permissions","network"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}