HumanSignal/label-studio · error · ValidationError
extract_message(exc)
Error message
extract_message(exc)
What it means
In GCSImportStorageSerializer.validate (and the export analog), after building the storage instance it calls storage.validate_connection(); any exception is converted to a DRF ValidationError whose message is extract_message(exc) — so the client sees the underlying Google client error text (auth, bucket not found, etc.) as field errors.
Source
Thrown at label_studio/io_storages/gcs/serializers.py:42
result.pop(attr)
return result
def validate(self, data):
data = super().validate(data)
storage = self.instance
if storage:
for key, value in data.items():
setattr(storage, key, value)
else:
if 'id' in self.initial_data:
storage_object = self.Meta.model.objects.get(id=self.initial_data['id'])
for attr in GCSImportStorageSerializer.secure_fields:
data[attr] = data.get(attr) or getattr(storage_object, attr)
storage = self.Meta.model(**data)
try:
storage.validate_connection()
except Exception as exc:
raise ValidationError(extract_message(exc))
return data
class GCSExportStorageSerializer(ExportStorageSerializer):
type = StorageTypeField(default=os.path.basename(os.path.dirname(__file__)))
def to_representation(self, instance):
result = super().to_representation(instance)
result.pop('google_application_credentials')
return result
class Meta:
model = GCSExportStorage
fields = '__all__'
View on GitHub (pinned to 0b49e9b539)
Solutions
- Read the extract_message text in the API error response — it names the real GCS failure (auth, 404 bucket, permission)
- Confirm the credentials JSON parses locally (python -m json.tool) and that private_key newlines are \n-escaped when passed as a string
- Verify the bucket exists and the service account has roles/storage.objectViewer and objectCreator (or objectAdmin)
- Test from the server: gsutil ls gs://<bucket> or gcs.Client(...).get_bucket(bucket) to reproduce outside Label Studio
Example fix
// before
{"google_application_credentials": "/local/path/key.json"} // path, not contents
// after (paste the JSON contents, properly escaped)
{"google_application_credentials": "{\"type\": \"service_account\", ... }", "bucket": "my-bucket"} Defensive patterns
Strategy: try-catch
Validate before calling
import json
def gcs_credentials_ok(creds_str, bucket):
try:
json.loads(creds_str)
except json.JSONDecodeError:
return False, 'credentials are not valid JSON'
from google.cloud import storage
try:
storage.Client.from_service_account_json.__self__ # import sanity
c = storage.Client.from_service_account_info(json.loads(creds_str))
c.get_bucket(bucket)
return True, None
except Exception as e:
return False, str(e) Type guard
def is_json_string(s):
import json
if not isinstance(s, str):
return False
try:
json.loads(s)
return True
except json.JSONDecodeError:
return False Try / catch
from rest_framework.exceptions import ValidationError
try:
api.save_gcs_storage(payload)
except ValidationError as e:
logger.error('GCS storage rejected: %s', e.detail) # extract_message shows the real GCS error Prevention
- Paste credentials file CONTENTS, not the path
- Validate with json.loads before submitting
- Check private_key \n escaping survives form submission
- Pre-verify bucket access with gsutil ls or get_bucket
When it happens
Trigger: Saving a GCS import/export storage via the API with invalid service-account credentials JSON, a nonexistent bucket, insufficient IAM permissions, or unreachable network — anything that makes validate_connection() raise.
Common situations: Pasting the whole Google credentials JSON with wrong escaping (newlines in private_key); bucket renamed/deleted; service account missing storage.buckets.get; workload without outbound access to storage.googleapis.com.
Related errors
- annotation_ser.errors
- Invalid or missing serializer class
- Google Application Credentials must be valid JSON string. {e
- No blobs found in {bucket_name}/{prefix} or prefix doesn't e
- Creating labels for different projects in one request not al
AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29).
Data as JSON: /api/errors/a500678f884d491b.
Report an issue: GitHub.