{"record":{"id":"d800176f4728f93b","repo":"HumanSignal/label-studio","slug":"you-do-not-have-permission-to-create-storages-for","errorCode":null,"errorMessage":"You do not have permission to create storages for this project.","messagePattern":"You do not have permission to create storages for this project\\.","errorType":"http","errorClass":"PermissionDenied","httpStatus":403,"severity":"error","filePath":"label_studio/io_storages/api.py","lineNumber":51,"sourceCode":"        project_pk = self.request.query_params.get('project')\n        if not project_pk:\n            raise ValidationError('query parameter \"project\" is required')\n\n        project = generics.get_object_or_404(Project, pk=project_pk)\n        self.check_object_permissions(self.request, project)\n        StorageClass = self.serializer_class.Meta.model\n        storages = StorageClass.objects.filter(project_id=project.id)\n\n        # check failed jobs and sync their statuses\n        StorageClass.ensure_storage_statuses(storages)\n        return storages\n\n    def perform_create(self, serializer):\n        from rest_framework.exceptions import PermissionDenied\n\n        project = serializer.validated_data.get('project')\n        if project is not None and not project.has_permission(self.request.user):\n            raise PermissionDenied('You do not have permission to create storages for this project.')\n        super().perform_create(serializer)\n\n\nclass ImportStorageDetailAPI(generics.RetrieveUpdateDestroyAPIView):\n    \"\"\"RUD storage by pk specified in URL\"\"\"\n\n    permission_required = ViewClassPermission(\n        GET=all_permissions.storages_view,\n        PATCH=all_permissions.storages_change,\n        PUT=all_permissions.storages_change,\n        DELETE=all_permissions.storages_change,\n    )\n    parser_classes = (JSONParser, FormParser, MultiPartParser)\n    serializer_class = ImportStorageSerializer\n\n    @extend_schema(exclude=True)\n    def put(self, request, *args, **kwargs):\n        return super(ImportStorageDetailAPI, self).put(request, *args, **kwargs)","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/io_storages/api.py#L33-L69","documentation":"DRF PermissionDenied raised in ImportStorageListAPI.perform_create when the authenticated user lacks access to the project referenced in the request payload. Label Studio checks project.has_permission(user) before allowing a storage to be attached, so storages cannot be created for projects the user is not a member of (or lacks storages_create permission on).","triggerScenarios":"POST to an import storage list endpoint (e.g. /api/storages/<type>/) with body containing a 'project' id for a project the requesting user cannot access; project passes a valid pk but has_permission returns False (not a project member, restricted role, or anonymous token).","commonSituations":"Using a service/account token that belongs to a different organization than the project; sharing a project id between workspaces; a user downgraded from admin/owner to reviewer/annotator attempting to attach S3/GCS/Azure storage.","solutions":["Log in as (or use a token for) a user who is a member of the target project with storage create permission","Verify the 'project' id in the request body matches a project in the same organization as the token","Grant the user a role with storages_create permission (admin/owner) on the project","Check organization membership of the API token via /api/current-user/whoami"],"exampleFix":"// before\ncurl -X POST /api/storages/s3/ -H \"Authorization: Token <user-without-access>\" -d '{\"project\": 5, ...}'\n// after\ncurl -X POST /api/storages/s3/ -H \"Authorization: Token <project-admin-token>\" -d '{\"project\": 5, ...}'","handlingStrategy":"validation","validationCode":"import requests\n# Check membership/permissions before POSTing a storage\nr = requests.get(f\"{LS_URL}/api/projects/{project_id}/\", headers={\"Authorization\": f\"Token {token}\"})\nif r.status_code == 404 or not r.ok:\n    raise PermissionError(f\"No access to project {project_id}\")","typeGuard":"def can_access_project(resp):\n    return resp.status_code == 200","tryCatchPattern":"try:\n    resp = requests.post(f\"{LS_URL}/api/storages/s3/\", json=payload, headers=headers)\n    resp.raise_for_status()\nexcept requests.HTTPError as e:\n    if resp.status_code == 403:\n        # switch to a token with storages_create permission\n        ...","preventionTips":["Use an admin/owner token for storage management automation","Confirm token's organization matches the project's organization","Check the user's role before attempting storage CRUD","Document which service accounts have storage permissions"],"tags":["permissions","drf","authorization","storage"],"backgroundTag":"insufficient-permissions","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}