{"record":{"id":"548c2418183d3879","repo":"redis/redis-py","slug":"himport-fields-must-be-a-collection-of-field-names","errorCode":null,"errorMessage":"HIMPORT fields must be a collection of field names, not a single string or binary value","messagePattern":"HIMPORT fields must be a collection of field names, not a single string or binary value","errorType":"validation","errorClass":"DataError","httpStatus":null,"severity":"error","filePath":"redis/himport.py","lineNumber":242,"sourceCode":"\n    @staticmethod\n    def _materialize_fields(fields: Iterable[FieldT]) -> tuple:\n        \"\"\"Validate and materialize the caller's field iterable into a tuple.\n\n        Done *before* the mutation lock is taken: consuming an arbitrary iterable\n        can be slow, or -- for a generator that inspects this same registry -- can\n        re-enter a locked read (e.g. ``yield`` then ``registry.names()``). Running\n        it under the non-reentrant ``_lock`` would stall every registry user or\n        deadlock permanently. Field order is preserved; nothing is reordered or\n        deduplicated.\n        \"\"\"\n        # A bare single field name (str/bytes/bytearray/memoryview) is itself\n        # iterable element-by-element; that is almost certainly a caller mistake and\n        # would silently register single-character/single-byte \"fields\" (e.g.\n        # memoryview(b\"id\") -> field names 105, 100), so reject it as invalid local\n        # API usage. int/float are not iterable, so tuple() below rejects them.\n        if isinstance(fields, (str, bytes, bytearray, memoryview)):\n            raise DataError(\n                \"HIMPORT fields must be a collection of field names, \"\n                \"not a single string or binary value\"\n            )\n        field_tuple = tuple(fields)\n        if not field_tuple:\n            raise DataError(\"HIMPORT fieldset must have at least one field\")\n        return field_tuple\n\n    def _set(self, name: str, field_tuple: tuple) -> HImportFieldset:\n        # ``field_tuple`` is already validated/materialized by\n        # :meth:`_materialize_fields`; only the (cheap, non-blocking) revision bump\n        # and dict mutation run here, so ``_lock`` is never held across arbitrary\n        # caller code.\n        fieldset = HImportFieldset(\n            name=name,\n            fields=field_tuple,\n            version=self._advance(),\n        )","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/redis/redis-py/blob/6a6b581b48225afa0b76912d1028c6035baee932/redis/himport.py#L224-L260","documentation":"_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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Always pass fields as a list/tuple/set of names: prepare('users', ['name', 'email']).","If you have a single field, wrap it: prepare('users', [field]).","Add a helper that normalizes a str/bytes into a one-element list before calling prepare."],"exampleFix":"// before\nregistry.prepare('users', 'email')\n// after\nregistry.prepare('users', ['email'])","handlingStrategy":"validation","validationCode":"def as_field_collection(fields):\n    if isinstance(fields, (str, bytes, bytearray, memoryview)):\n        return [fields]\n    return list(fields)\n# then: registry.prepare('users', as_field_collection(fields))","typeGuard":"def is_field_collection(fields) -> bool:\n    return not isinstance(fields, (str, bytes, bytearray, memoryview))","tryCatchPattern":null,"preventionTips":["Always pass a list/tuple of field names to HImportRegistry.prepare.","Wrap a single field in a one-element list.","Add a helper that normalizes scalar fields before calling prepare."],"tags":["himport","validation","data-error","fields"],"backgroundTag":null,"analyzedSha":"6a6b581b48225afa0b76912d1028c6035baee932","analyzedAt":"2026-08-10T12:52:44.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}