{"record":{"id":"a354fdb04254facf","repo":"BerriAI/litellm","slug":"access-denied-to-directory-directory-path-che-a354fd","errorCode":null,"errorMessage":"Access denied to directory '{directory_path}'. Check your GitLab permissions for project '{self.project}'.","messagePattern":"Access denied to directory '(.+?)'\\. Check your GitLab permissions for project '(.+?)'\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/integrations/gitlab/gitlab_client.py","lineNumber":225,"sourceCode":"            if resp.status_code == 404:\n                return []\n            resp.raise_for_status()\n\n            data: Final = resp.json() or []\n            files: Final[list[str]] = []\n            for item in data:\n                if item.get(\"type\") == \"blob\":\n                    file_path = item.get(\"path\", \"\")\n                    if not file_extension or file_path.endswith(file_extension):\n                        files.append(file_path)\n            return files\n\n        except Exception as e:\n            status: Final = getattr(getattr(e, \"response\", None), \"status_code\", None)\n            if status == 404:\n                return []\n            if status == 403:\n                raise Exception(\n                    f\"Access denied to directory '{directory_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 list files in '{directory_path}': {e}\")\n\n    def get_repository_info(self) -> dict[str, Any]:\n        \"\"\"Get information about the project/repository.\"\"\"\n        url: Final = f\"{self.base_url}/projects/{self._project_enc}\"\n        try:\n            resp: Final = self.http_handler.get(url, headers=self.headers)\n            resp.raise_for_status()\n            return resp.json()\n        except Exception as e:\n            raise Exception(f\"Failed to get repository info: {e}\")\n\n    def test_connection(self) -> bool:\n        \"\"\"Test the connection to the GitLab project.\"\"\"","sourceCodeStart":207,"sourceCodeEnd":243,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/integrations/gitlab/gitlab_client.py#L207-L243","documentation":"GitLab answered 403 Forbidden for the repository tree API used by list_files (GET /projects/<id>/repository/tree?ref=...). The token authenticated but cannot read this project/directory: role below Reporter on a private project, missing read_repository scope, or a path under a repo the identity cannot see. Note that a 404 returns an empty list instead of raising, so this error specifically means the identity is being denied, not that the path is missing.","triggerScenarios":"list_files('prompts') with a Guest-role token; PAT without read_repository scope; directory path belonging to a different (private) subgroup repo; protected ref with an unprivileged token.","commonSituations":"Startup prompt discovery running with a least-privilege CI token; org restructures moving repositories between groups; scope changes after token rotation.","solutions":["Confirm the token's user can browse that directory in the GitLab UI","Use read_repository scope and Reporter+ role on the project","Check the directory path spelling/case and that it lives in the configured project","Verify you pointed at the right project (a wrong project that exists and is private yields 403)"],"exampleFix":"# before: token without repository read on a private project\nfiles = client.list_files(\"prompts/internal\")\n\n# after: token with read_repository scope and a path the identity can see\nfiles = client.list_files(\"prompts\")","handlingStrategy":"try-catch","validationCode":"# Fail fast at startup instead of discovering 403s during prompt listing\nif not client.test_connection():\n    raise RuntimeError(\"GitLab credentials/project invalid\")\nclient.list_files(\"prompts\")  # exercises the tree API at boot","typeGuard":null,"tryCatchPattern":"try:\n    files = client.list_files(\"prompts\")\nexcept Exception as e:\n    if \"Access denied to directory\" in str(e):\n        raise RuntimeError(\n            \"Token cannot list the prompts directory - \"\n            \"grant read_repository / Reporter+ or fix the path\"\n        ) from e\n    raise","preventionTips":["Exercise the tree listing once during deployment as a permission check","Keep prompts in one directory the integration token can read","Review scopes after every GitLab token rotation"],"tags":["gitlab","permissions","http-403","forbidden","tree-api"],"backgroundTag":"http-403-forbidden","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}