HumanSignal/label-studio · error · ValidationError

{storage.url_scheme}://{storage.bucket}/{storage.prefix} not

Error message

{storage.url_scheme}://{storage.bucket}/{storage.prefix} not found.

What it means

The final except KeyError in S3StorageSerializerMixin.validate() surfaces the KeyError raised inside S3Storage.validate_connection() as ValidationError(f'{storage.url_scheme}://{storage.bucket}/{storage.prefix} not found.'). It reaches the API caller with the actual storage's scheme/bucket/prefix interpolated, meaning the bucket+prefix combination had no matching keys (see error 162).

Source

Thrown at label_studio/io_storages/s3/serializers.py:74

            if (
                e.response.get('Error').get('Code') in ['SignatureDoesNotMatch', '403']
                or e.response.get('ResponseMetadata').get('HTTPStatusCode') == 403
            ):
                raise ValidationError(
                    'Cannot connect to S3 {bucket_name} with specified AWS credentials'.format(
                        bucket_name=storage.bucket
                    )
                )
            if (
                e.response.get('Error').get('Code') in ['NoSuchBucket', '404']
                or e.response.get('ResponseMetadata').get('HTTPStatusCode') == 404
            ):
                raise ValidationError('Cannot find bucket {bucket_name} in S3'.format(bucket_name=storage.bucket))
        except TypeError as e:
            logger.info(f'It seems access keys are incorrect: {e}', exc_info=True)
            raise ValidationError('It seems access keys are incorrect')
        except KeyError:
            raise ValidationError(f'{storage.url_scheme}://{storage.bucket}/{storage.prefix} not found.')
        return data


class S3ImportStorageSerializer(S3StorageSerializerMixin, ImportStorageSerializer):
    type = StorageTypeField(default=os.path.basename(os.path.dirname(__file__)))
    presign = serializers.BooleanField(required=False, default=True)

    def validate_s3_endpoint(self, value):
        if value and settings.SSRF_PROTECTION_ENABLED:
            validate_url_for_ssrf(value, block_local_urls=True)
        return value

    class Meta:
        model = S3ImportStorage
        fields = '__all__'


class S3ExportStorageSerializer(S3StorageSerializerMixin, ExportStorageSerializer):

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Upload at least one object under bucket/prefix before connecting the import storage.
  2. Correct or remove the `prefix` field so it matches where files actually live.
  3. Verify bucket/endpoint/region so listing hits the intended bucket.

Example fix

// before
{"bucket": "my-bucket", "prefix": "imports/"}   // bucket has files at root

// after
{"bucket": "my-bucket", "prefix": ""}
Defensive patterns

Strategy: validation

Validate before calling

import boto3

def prefix_keycount(bucket: str, prefix: str, **client_kwargs) -> int:
    s3 = boto3.client('s3', **client_kwargs)
    return s3.list_objects_v2(Bucket=bucket, Prefix=prefix, MaxKeys=1).get('KeyCount', 0)

if prefix_keycount('my-bucket', 'imports/') == 0:
    raise ValueError('No objects under bucket/prefix; fix prefix or upload data first')

Type guard

def prefix_matches_layout(storage: dict) -> bool:
    p = storage.get('prefix', '')
    return p == '' or bool(storage.get('bucket'))

Try / catch

try:
    serializer.is_valid(raise_exception=True)
except ValidationError as e:
    msg = str(e.detail)
    if 'not found' in msg and '://' in msg:
        # KeyError from validate_connection: bucket/prefix had zero keys
        print(f'No objects at {msg}; adjust prefix or upload files')

Prevention

When it happens

Trigger: Any import-storage create/update where validate_connection()'s ListObjectsV2 returns KeyCount 0/None for the given bucket and prefix, triggering the KeyError that this handler converts.

Common situations: Same as prefix-not-found: empty bucket, wrong prefix, objects at root while a prefix is set, endpoint/region mismatch returning empty result sets.

Related errors


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