HumanSignal/label-studio · error · DRFValidationError

extract_message(exc)

Error message

extract_message(exc)

What it means

For exceptions that are not Django/DRF ValidationErrors (e.g. ValueError from missing path, or unexpected errors from validate_connection), the Local Files import serializer converts them with extract_message and re-raises DRF ValidationError. This guarantees the API always returns a clean 400 message string instead of leaking a 500 stack trace. The message is the extracted str of the original exception.

Source

Thrown at label_studio/io_storages/localfiles/serializers.py:44

    type = StorageTypeField(default=os.path.basename(os.path.dirname(__file__)))

    class Meta:
        model = LocalFilesImportStorage
        fields = '__all__'

    def validate(self, data):
        # Validate local file path
        data = super(LocalFilesImportStorageSerializer, self).validate(data)
        if 'path' in data:
            data['path'] = normalize_storage_path(data['path'])
        storage = LocalFilesImportStorage(**data)
        try:
            storage.validate_connection()
        except (DjangoValidationError, DRFValidationError) as exc:
            detail = getattr(exc, 'detail', getattr(exc, 'messages', str(exc)))
            raise DRFValidationError(_stringify_detail(detail))
        except Exception as exc:
            raise DRFValidationError(extract_message(exc))
        return data


class LocalFilesExportStorageSerializer(ExportStorageSerializer):
    type = StorageTypeField(default=os.path.basename(os.path.dirname(__file__)))

    class Meta:
        model = LocalFilesExportStorage
        fields = '__all__'

    def validate(self, data):
        # Validate local file path
        data = super(LocalFilesExportStorageSerializer, self).validate(data)
        if 'path' in data:
            data['path'] = normalize_storage_path(data['path'])
        storage = LocalFilesExportStorage(**data)
        try:
            storage.validate_connection()

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Include a non-empty 'path' field in the request payload (normalize_storage_path strips slashes; an empty/whitespace path triggers this)
  2. Read the returned 400 message — it is the original exception's message and points to the actual problem
  3. Fix whatever underlying condition extract_message reports (set the path, check the server environment/settings)

Example fix

// before
POST /api/storages/localfiles/ {"project": 1}   // no path -> 400 'Path must be set for Local Files storage'
// after
POST /api/storages/localfiles/ {"project": 1, "path": "/label-studio/data/dataset1"}
Defensive patterns

Strategy: try-catch

Validate before calling

payload = {"project": 1, "path": "/label-studio/data/dataset1"}
assert payload.get('path', '').strip(), "'path' is required for localfiles storage"

Try / catch

try:
    resp = requests.post(f'{host}/api/storages/localfiles/', json=payload)
    resp.raise_for_status()
except requests.HTTPError:
    print('Validation message:', resp.json().get('detail'))

Prevention

When it happens

Trigger: POST/PATCH /api/storages/localfiles/ where storage.validate_connection raises a non-ValidationError: path missing entirely ('Path must be set for Local Files storage' ValueError) or any unexpected exception during connection validation.

Common situations: Omitting the 'path' field in the storage payload; client bugs sending null path; environment problems (unreadable settings) surfacing as generic extracted messages in a 400.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of HumanSignal/label-studio@0b49e9b539 (2026-08-29). Data as JSON: /api/errors/6404c039c5986a98. Report an issue: GitHub.