redis/redis-py · error · DataError

HIMPORT fieldset must have at least one field

Error message

HIMPORT fieldset must have at least one field

What it means

_materialize_fields (redis/himport.py:248) rejects an empty field collection with DataError('HIMPORT fieldset must have at least one field'). After converting the caller's iterable to a tuple, a zero-length result is invalid because an HIMPORT fieldset with no fields has no server-side meaning and would produce a no-op/erroneous HIMPORT PREPARE.

Solutions

  1. Ensure the field iterable contains at least one name before calling prepare.
  2. Skip the prepare call (and the HIMPORT feature) entirely when the field set is empty.
  3. Add a config-time assertion so an empty schema fails loudly during startup, not at prepare time.

Example fix

// before
registry.prepare('users', fields_from_config)
// after
if fields_from_config:
    registry.prepare('users', fields_from_config)
else:
    log.warning('no HIMPORT fields configured for users; skipping')
Defensive patterns

Strategy: validation

Validate before calling

fields = list(fields_src)
assert fields, 'HIMPORT fieldset must have at least one field'
if fields:
    registry.prepare('users', fields)

Type guard

def has_fields(fields) -> bool:
    return not isinstance(fields, (str, bytes, bytearray, memoryview)) and len(list(fields)) > 0

Prevention

When it happens

Trigger: registry.prepare('users', []) ; prepare('users', ()) ; passing an empty generator; passing a list that was filtered down to zero elements.

Common situations: Computing the field list from a config/schema that happened to be empty in a test/staging environment; a filter that removed everything; defaulting fields to [] when none configured.

Related errors


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

Appendix: source

Thrown at redis/himport.py:248

        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(),
        )
        self._fieldsets[name] = fieldset
        return fieldset

    # -- mutation ---------------------------------------------------------

    def prepare(self, name: str, fields: Iterable[FieldT]) -> HImportFieldset:

View on GitHub (pinned to 6a6b581b48)