HumanSignal/label-studio · error · PermissionDenied
PermissionDenied
Error message
PermissionDenied
What it means
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.
Source
Thrown at label_studio/io_storages/functions.py:46
serializer_class: The serializer class to use for validation
Returns:
The prepared storage instance
Raises:
PermissionDenied: If user doesn't have permission to access the storage
ValidationError: If serializer validation fails
"""
if not serializer_class or not hasattr(serializer_class, 'Meta'):
raise ValidationError('Invalid or missing serializer class')
storage_id = request.data.get('id')
instance = None
if storage_id:
instance = get_object_or_404(serializer_class.Meta.model.objects.all(), pk=storage_id)
if not instance.has_permission(request.user):
raise PermissionDenied()
# combine instance fields with request.data
serializer = serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
# if storage exists, we have to use instance from DB,
# because instance from serializer won't have credentials, they were popped intentionally
if instance:
instance = serializer.update(instance, serializer.validated_data)
else:
instance = serializer_class.Meta.model(**serializer.validated_data)
# double check: not all storages validate connection in serializer, just make another explicit check here
try:
instance.validate_connection()
except Exception as exc:
logger.error(f'Error validating storage connection: {exc}')
raise ValidationError('Error validating storage connection')View on GitHub (pinned to 0b49e9b539)
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
Example fix
# before (id belongs to another user)
curl -H "Authorization: Token <other-user-token>" -d '{"id": 42, ...}' /api/storages/s3/
# after (omit id to create your own, or use the owner's token)
curl -H "Authorization: Token <owner-token>" -d '{"id": 42, ...}' /api/storages/s3/ Defensive patterns
Strategy: try-catch
Validate before calling
def can_access_storage(user, storage):
return storage.has_permission(user) Try / catch
from rest_framework.exceptions import PermissionDenied
try:
resp = requests.post(url, json=payload, headers=auth)
resp.raise_for_status()
except PermissionDenied:
logger.error('Storage id %s not owned by this user — omit id or use the owner token') Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- You can delete members only for your current active organiza
- You do not have permission to create storages for this proje
- Invalid or missing serializer class
- Authentication token no longer valid: legacy token authentic
- token_exists
AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29).
Data as JSON: /api/errors/bfda03ee6de4c011.
Report an issue: GitHub.