HumanSignal/label-studio · error · NotImplementedError

NotImplementedError

Error message

NotImplementedError

What it means

ImportStorage.iter_objects is the abstract hook a backend must implement to yield storage objects during a synchronization scan. The base class raises NotImplementedError, so calling it on a subclass without an override (or on the base class itself) always fails. It is invoked by ImportStorage.create / sync paths when linking tasks.

Source

Thrown at label_studio/io_storages/base_models.py:330

    description = models.TextField(_('description'), null=True, blank=True, help_text='Cloud storage description')
    created_at = models.DateTimeField(_('created at'), auto_now_add=True, help_text='Creation time')

    synchronizable = models.BooleanField(_('synchronizable'), default=True, help_text='If storage can be synced')

    def validate_connection(self, client=None):
        raise NotImplementedError('validate_connection is not implemented')

    class Meta:
        abstract = True


class ImportStorage(Storage):
    def iter_objects(self) -> Iterator[Any]:
        """
        Returns:
            Iterator[Any]: An iterator for objects in the storage.
        """
        raise NotImplementedError

    def iter_keys(self) -> Iterator[str]:
        """
        Returns:
            Iterator[str]: An iterator of keys for each object in the storage.
        """
        raise NotImplementedError

    def get_unified_metadata(self, obj: Any) -> dict:
        """
        Args:
            obj: The storage object to get metadata for
        Returns:
            dict: A dictionary of metadata for the object with keys:
            'key', 'last_modified', 'size'.
        """
        raise NotImplementedError

View on GitHub (pinned to 0b49e9b539)

Solutions

  1. Implement iter_objects in your ImportStorage subclass to yield objects (e.g. bucket object wrappers) from your backend.
  2. If your backend is key-listing only, implement both iter_keys and iter_objects so either scan path works.
  3. Verify the storage you synced is the concrete subclass (e.g. S3ImportStorage), not the abstract ImportStorage base.

Example fix

// before
class MyImportStorage(ImportStorage):
    pass

// after
class MyImportStorage(ImportStorage):
    def iter_objects(self):
        for obj in self.client.list_objects():
            yield obj
Defensive patterns

Strategy: validation

Validate before calling

import inspect
if inspect.isabstract(type(storage)) or type(storage).iter_objects is ImportStorage.iter_objects:
    raise TypeError(f'{type(storage).__name__} does not implement iter_objects')

Type guard

def supports_object_iteration(storage) -> bool:
    return type(storage).iter_objects is not ImportStorage.iter_objects

Try / catch

try:
    for obj in storage.iter_objects():
        process(obj)
except NotImplementedError:
    logger.error('%s cannot be synced: iter_objects not implemented', type(storage).__name__)

Prevention

When it happens

Trigger: Running a sync (_scan_and_create_links -> create -> iter_objects) on a custom ImportStorage subclass that does not implement iter_objects; instantiating ImportStorage directly and syncing.

Common situations: Custom storage integration missing the iter_objects method; refactoring moved the implementation into iter_keys only while the sync path still relies on iter_objects.

Related errors


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