{"record":{"id":"9f13b809e6dcb995","repo":"HumanSignal/label-studio","slug":"underlying-connection-validation-error","errorCode":null,"errorMessage":"{underlying connection validation error}","messagePattern":"\\{underlying connection validation error\\}","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"label_studio/io_storages/api.py","lineNumber":107,"sourceCode":"\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\n        # double check: not export storages don't validate connection in serializer,\n        # just make another explicit check here, note: in this create API we have credentials in request.data\n        instance = serializer.Meta.model(**serializer.validated_data)\n        try:\n            instance.validate_connection()\n        except Exception as exc:\n            raise ValidationError(exc)\n\n        storage = serializer.save()\n        if settings.SYNC_ON_TARGET_STORAGE_CREATION:\n            storage.sync()\n\n\nclass ExportStorageDetailAPI(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 = ExportStorageSerializer\n","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/io_storages/api.py#L89-L125","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\n{\"bucket\": \"my-bucket\", \"aws_access_key_id\": \"AKIA...\", \"aws_secret_access_key\": \"<old-rotated-key>\"}  // 400 SignatureDoesNotMatch\n// after\n{\"bucket\": \"my-bucket\", \"aws_access_key_id\": \"AKIA...\", \"aws_secret_access_key\": \"<current-key>\"}  // 201","handlingStrategy":"try-catch","validationCode":"# pre-validate credentials with the cloud CLI before POSTing\nimport subprocess\nsubprocess.run([\"aws\", \"s3\", \"ls\", f\"s3://{bucket}\"], check=True)","typeGuard":"def storage_payload_has_credentials(p):\n    return bool(p.get(\"aws_access_key_id\") and p.get(\"aws_secret_access_key\") and p.get(\"bucket\"))","tryCatchPattern":"try:\n    resp = requests.post(export_storage_url, json=payload, headers=headers)\n    resp.raise_for_status()\nexcept requests.HTTPError as e:\n    detail = resp.json().get(\"detail\", \"\")  # wrapped validate_connection error\n    logging.error(\"Storage connection failed: %s\", detail)","preventionTips":["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"],"tags":["cloud-storage","validation","connection","aws","azure","gcp"],"backgroundTag":"storage-connection-validation-failed","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}