HumanSignal/label-studio · error · KeyError

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

Error message

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

What it means

S3Storage.validate_connection() tests the bucket with ListObjectsV2 (MaxKeys=1) when a prefix is set. For imports it expects at least one key under bucket/prefix; if KeyCount is 0 (or missing) it raises KeyError('<scheme>://<bucket>/<prefix> not found.'). This means the bucket exists but contains no objects under that prefix (or the prefix is misspelled).

Source

Thrown at label_studio/io_storages/s3/models.py:116

            self.validate_connection(client)
        return client, s3.Bucket(self.bucket)

    @catch_and_reraise_from_none
    def validate_connection(self, client=None):
        logger.debug('validate_connection')
        if client is None:
            client = self.get_client()
        # TODO(jo): add check for write access for .*Export.* classes
        is_export = 'Export' in self.__class__.__name__
        if self.prefix:
            logger.debug(
                f'[Class {self.__class__.__name__}]: Test connection to bucket {self.bucket} with prefix {self.prefix} using ListObjectsV2 operation'
            )
            result = client.list_objects_v2(Bucket=self.bucket, Prefix=self.prefix, MaxKeys=1)
            # We expect 1 key with the prefix for imports. For exports it's okay if there are 0 with the prefix.
            expected_keycount = 0 if is_export else 1
            if (keycount := result.get('KeyCount')) is None or keycount < expected_keycount:
                raise KeyError(f'{self.url_scheme}://{self.bucket}/{self.prefix} not found.')
        else:
            logger.debug(
                f'[Class {self.__class__.__name__}]: Test connection to bucket {self.bucket} using HeadBucket operation'
            )
            client.head_bucket(Bucket=self.bucket)

    @property
    def path_full(self):
        prefix = self.prefix or ''
        return f'{self.url_scheme}://{self.bucket}/{prefix}'

    @property
    def type_full(self):
        return 'Amazon AWS S3'

    @catch_and_reraise_from_none
    def get_bytes_stream(self, uri, range_header=None):
        """Get file directly from S3 using iter_chunks without wrapper.

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Verify the bucket actually contains objects under the given prefix (aws s3 ls s3://<bucket>/<prefix>/).
  2. Correct the `prefix` field in the storage config, or clear it if objects live at the bucket root.
  3. Check region/endpoint config so ListObjectsV2 queries the right bucket.
  4. If the bucket is intentionally empty, connect after uploading files or use an export storage.

Example fix

// before
{"type": "s3", "bucket": "my-tasks", "prefix": "taskz/"}   // typo

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

Strategy: validation

Validate before calling

import boto3

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

# run before creating the storage
assert prefix_has_objects('my-bucket', 'tasks/')

Type guard

def has_valid_prefix(storage: dict) -> bool:
    return isinstance(storage.get('prefix', ''), str)

Try / catch

try:
    storage.save()
except KeyError as e:
    if 'not found' in str(e):
        print(f'{bucket}/{prefix} has no objects; fix prefix or upload files')

Prevention

When it happens

Trigger: Creating/updating an S3 import storage via validate() with a `prefix` that matches zero objects; also reachable through get_client_and_bucket when prefix-based validation runs.

Common situations: Typo in prefix or bucket; files stored without the expected prefix (e.g. uploaded to bucket root while prefix='data/'); region mismatch returning empty listings; bucket actually empty at connect time; export storages are exempt (expected count 0).

Related errors


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