HumanSignal/label-studio · error · ValidationError
{underlying connection validation error}
Error message
{underlying connection validation error} What it means
After permission checks, ExportStorageListAPI.perform_create instantiates the storage model and calls validate_connection(); any exception (auth failure, missing container, network error) is re-raised as a DRF ValidationError wrapping the underlying message. It exists because export storage serializers skip connection validation, so the view performs an explicit check with the credentials from request.data.
Solutions
- Read the wrapped message in the response detail — it names the real cloud error
- Test the same credentials with the cloud CLI (aws s3 ls / gsutil ls / az storage container list)
- Fix the credential fields (access key, secret, container/bucket name, prefix) and retry
- Ensure network egress to the provider endpoint and correct region/endpoint URL
- Grant the IAM identity permissions to list the target container
Example fix
// before
{"bucket": "my-bucket", "aws_access_key_id": "AKIA...", "aws_secret_access_key": "<old-rotated-key>"} // 400 SignatureDoesNotMatch
// after
{"bucket": "my-bucket", "aws_access_key_id": "AKIA...", "aws_secret_access_key": "<current-key>"} // 201 Defensive patterns
Strategy: try-catch
Validate before calling
# pre-validate credentials with the cloud CLI before POSTing
import subprocess
subprocess.run(["aws", "s3", "ls", f"s3://{bucket}"], check=True) Type guard
def storage_payload_has_credentials(p):
return bool(p.get("aws_access_key_id") and p.get("aws_secret_access_key") and p.get("bucket")) Try / catch
try:
resp = requests.post(export_storage_url, json=payload, headers=headers)
resp.raise_for_status()
except requests.HTTPError as e:
detail = resp.json().get("detail", "") # wrapped validate_connection error
logging.error("Storage connection failed: %s", detail) Prevention
- Run validate endpoint before create when supported
- Rotate credentials in Label Studio immediately after cloud-side rotation
- Grant IAM list/get permissions on the target container
- Verify network egress to the cloud provider from the Label Studio host
When it happens
Trigger: POST creating an export storage whose credentials are wrong (bad access key, missing account name/key, nonexistent container, expired token), causing validate_connection() to throw.
Common situations: Typo'd AWS secret, rotated cloud credentials not yet updated in Label Studio, storage target bucket deleted or renamed, VPC/firewall blocking outbound calls to the cloud provider, IAM policy lacking list/get on the bucket.
Related errors
- {connection validation error}
- Container not found
- :// / not found.
- exc.kwargs['report']
- Wrong credentials for S3
AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29).
Data as JSON: /api/errors/9f13b809e6dcb995.
Report an issue: GitHub.
Appendix: source
Thrown at label_studio/io_storages/api.py:107
# check failed jobs and sync their statuses
StorageClass.ensure_storage_statuses(storages)
return storages
def perform_create(self, serializer):
from rest_framework.exceptions import PermissionDenied
project = serializer.validated_data.get('project')
if project is not None and not project.has_permission(self.request.user):
raise PermissionDenied('You do not have permission to create storages for this project.')
# double check: not export storages don't validate connection in serializer,
# just make another explicit check here, note: in this create API we have credentials in request.data
instance = serializer.Meta.model(**serializer.validated_data)
try:
instance.validate_connection()
except Exception as exc:
raise ValidationError(exc)
storage = serializer.save()
if settings.SYNC_ON_TARGET_STORAGE_CREATION:
storage.sync()
class ExportStorageDetailAPI(generics.RetrieveUpdateDestroyAPIView):
"""RUD storage by pk specified in URL"""
permission_required = ViewClassPermission(
GET=all_permissions.storages_view,
PATCH=all_permissions.storages_change,
PUT=all_permissions.storages_change,
DELETE=all_permissions.storages_change,
)
parser_classes = (JSONParser, FormParser, MultiPartParser)
serializer_class = ExportStorageSerializer
View on GitHub (pinned to 0b49e9b539)