redis/redis-py · error · DataError

HIMPORT fields must be a collection of field names, not a…

Error message

HIMPORT fields must be a collection of field names, not a single string or binary value

What it means

_materialize_fields (redis/himport.py:242) rejects a fields argument that is a single str/bytes/bytearray/memoryview, because those types are iterable element-by-element and would otherwise silently register each character/byte as a separate 'field' (e.g. memoryview(b'id') -> fields 105, 100). It raises a DataError telling you to pass a collection of field names instead. Called by HImportRegistry.prepare before the mutation lock is taken.

Solutions

  1. Always pass fields as a list/tuple/set of names: prepare('users', ['name', 'email']).
  2. If you have a single field, wrap it: prepare('users', [field]).
  3. Add a helper that normalizes a str/bytes into a one-element list before calling prepare.

Example fix

// before
registry.prepare('users', 'email')
// after
registry.prepare('users', ['email'])
Defensive patterns

Strategy: validation

Validate before calling

def as_field_collection(fields):
    if isinstance(fields, (str, bytes, bytearray, memoryview)):
        return [fields]
    return list(fields)
# then: registry.prepare('users', as_field_collection(fields))

Type guard

def is_field_collection(fields) -> bool:
    return not isinstance(fields, (str, bytes, bytearray, memoryview))

Prevention

When it happens

Trigger: registry.prepare('users', 'name') (a bare string instead of ['name']); registry.prepare('k', b'a') or a memoryview; any call passing a single field name where an iterable of names is expected.

Common situations: Misreading the API and assuming prepare takes one field name; a helper that forwards a single optional field without wrapping it in a list; refactoring from a list-of-one to a scalar.

Related errors


AI-assisted analysis of redis/redis-py@6a6b581b48 (2026-08-10). Data as JSON: /api/errors/548c2418183d3879. Report an issue: GitHub.

Appendix: source

Thrown at redis/himport.py:242

    @staticmethod
    def _materialize_fields(fields: Iterable[FieldT]) -> tuple:
        """Validate and materialize the caller's field iterable into a tuple.

        Done *before* the mutation lock is taken: consuming an arbitrary iterable
        can be slow, or -- for a generator that inspects this same registry -- can
        re-enter a locked read (e.g. ``yield`` then ``registry.names()``). Running
        it under the non-reentrant ``_lock`` would stall every registry user or
        deadlock permanently. Field order is preserved; nothing is reordered or
        deduplicated.
        """
        # A bare single field name (str/bytes/bytearray/memoryview) is itself
        # iterable element-by-element; that is almost certainly a caller mistake and
        # would silently register single-character/single-byte "fields" (e.g.
        # memoryview(b"id") -> field names 105, 100), so reject it as invalid local
        # API usage. int/float are not iterable, so tuple() below rejects them.
        if isinstance(fields, (str, bytes, bytearray, memoryview)):
            raise DataError(
                "HIMPORT fields must be a collection of field names, "
                "not a single string or binary value"
            )
        field_tuple = tuple(fields)
        if not field_tuple:
            raise DataError("HIMPORT fieldset must have at least one field")
        return field_tuple

    def _set(self, name: str, field_tuple: tuple) -> HImportFieldset:
        # ``field_tuple`` is already validated/materialized by
        # :meth:`_materialize_fields`; only the (cheap, non-blocking) revision bump
        # and dict mutation run here, so ``_lock`` is never held across arbitrary
        # caller code.
        fieldset = HImportFieldset(
            name=name,
            fields=field_tuple,
            version=self._advance(),
        )

View on GitHub (pinned to 6a6b581b48)