{"record":{"id":"bfda03ee6de4c011","repo":"HumanSignal/label-studio","slug":"permissiondenied","errorCode":null,"errorMessage":"PermissionDenied","messagePattern":"PermissionDenied","errorType":"http","errorClass":"PermissionDenied","httpStatus":403,"severity":"error","filePath":"label_studio/io_storages/functions.py","lineNumber":46,"sourceCode":"        serializer_class: The serializer class to use for validation\n\n    Returns:\n        The prepared storage instance\n\n    Raises:\n        PermissionDenied: If user doesn't have permission to access the storage\n        ValidationError: If serializer validation fails\n    \"\"\"\n    if not serializer_class or not hasattr(serializer_class, 'Meta'):\n        raise ValidationError('Invalid or missing serializer class')\n\n    storage_id = request.data.get('id')\n    instance = None\n\n    if storage_id:\n        instance = get_object_or_404(serializer_class.Meta.model.objects.all(), pk=storage_id)\n        if not instance.has_permission(request.user):\n            raise PermissionDenied()\n\n    # combine instance fields with request.data\n    serializer = serializer_class(data=request.data)\n    serializer.is_valid(raise_exception=True)\n\n    # if storage exists, we have to use instance from DB,\n    # because instance from serializer won't have credentials, they were popped intentionally\n    if instance:\n        instance = serializer.update(instance, serializer.validated_data)\n    else:\n        instance = serializer_class.Meta.model(**serializer.validated_data)\n\n    # double check: not all storages validate connection in serializer, just make another explicit check here\n    try:\n        instance.validate_connection()\n    except Exception as exc:\n        logger.error(f'Error validating storage connection: {exc}')\n        raise ValidationError('Error validating storage connection')","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/io_storages/functions.py#L28-L64","documentation":"The storage view raises DRF PermissionDenied when the storage instance found by id exists but instance.has_permission(request.user) returns False, i.e. the requesting user does not own or have access to that storage object.","triggerScenarios":"POSTing to a storage create/update endpoint with an 'id' belonging to another user/organization, or after a user's role changed and they no longer have access to the project that owns the storage.","commonSituations":"Copying API calls between accounts/teams without changing the storage id; members of an organization trying to reuse an admin-created storage; id left in the request body from a copied curl command.","solutions":["Omit the 'id' field to create a new storage owned by the requesting user instead of reusing someone else's","Log in as, or use the API token of, the user who owns the storage (check org membership and project role)","Have an admin verify has_permission in the storage model — usually requires same project/organization access","Create a fresh storage under your own project and re-point your tooling at its id"],"exampleFix":"# before (id belongs to another user)\ncurl -H \"Authorization: Token <other-user-token>\" -d '{\"id\": 42, ...}' /api/storages/s3/\n# after (omit id to create your own, or use the owner's token)\ncurl -H \"Authorization: Token <owner-token>\" -d '{\"id\": 42, ...}' /api/storages/s3/","handlingStrategy":"try-catch","validationCode":"def can_access_storage(user, storage):\n    return storage.has_permission(user)","typeGuard":null,"tryCatchPattern":"from rest_framework.exceptions import PermissionDenied\ntry:\n    resp = requests.post(url, json=payload, headers=auth)\n    resp.raise_for_status()\nexcept PermissionDenied:\n    logger.error('Storage id %s not owned by this user — omit id or use the owner token')","preventionTips":["Never copy another user's storage id into requests","Use the API token of the storage owner or an org admin","Verify project/organization membership before sharing storage ids","Omit 'id' to create a fresh storage owned by the current user"],"tags":["permissions","api","auth","drf"],"backgroundTag":"permission-denied","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}