{"record":{"id":"cb05f512724bceb2","repo":"docling-project/docling","slug":"record-selectors-must-be-unique","errorCode":null,"errorMessage":"record selectors must be unique","messagePattern":"record selectors must be unique","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"docling/datamodel/backend_options.py","lineNumber":566,"sourceCode":"            return self.records[0]\n        return next(\n            (item for item in self.records if item.selector == record_type), None\n        )\n\n    @model_validator(mode=\"after\")\n    def _validate_records(self) -> \"EbcdicLayout\":\n        if len(self.records) > 1 and self.record_type_field is None:\n            raise ValueError(\n                \"record_type_field is required for a layout with several records\"\n            )\n        if self.record_type_field is not None:\n            selectors = [item.selector for item in self.records]\n            if None in selectors:\n                raise ValueError(\n                    \"every record needs a selector when record_type_field is set\"\n                )\n            if len(set(selectors)) != len(selectors):\n                raise ValueError(\"record selectors must be unique\")\n        return self\n\n\nclass EbcdicBackendOptions(BaseBackendOptions):\n    \"\"\"Options specific to the EBCDIC backend.\"\"\"\n\n    kind: Annotated[Literal[\"ebcdic\"], Field(exclude=True, repr=False)] = \"ebcdic\"\n    encoding: Annotated[\n        str,\n        Field(\n            description=(\n                \"Python codec used to decode character data, e.g. `cp037` \"\n                \"(US/Canada), `cp500` (international) or `cp1140` (euro).\"\n            )\n        ),\n    ] = \"cp037\"\n    layout: Annotated[\n        Optional[EbcdicLayout], Field(description=\"Parsing rules for the file.\")","sourceCodeStart":548,"sourceCodeEnd":584,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/datamodel/backend_options.py#L548-L584","documentation":"Pydantic model_validator on EbcdicLayout: selectors across records must be unique. Duplicate selectors make select()'s next() ambiguous (first match silently wins), so the model rejects the layout rather than allow silent misinterpretation of records.","triggerScenarios":"Two EbcdicRecordLayout entries with the same selector string (e.g., both 'D') while record_type_field is set; copy-pasting a record definition and changing fields but not selector; data-entry typos like 'O' vs '0' colliding after normalization.","commonSituations":"Hand-maintained layout files for legacy EBCDIC exports where record-type codes were duplicated by mistake; generated layouts from a spreadsheet where two rows share a type code.","solutions":["Give each record a distinct selector value matching the actual codes in the file.","If two codes map to the same schema, either merge them or use distinct selectors and adjust the source data expectation.","Programmatically check len({r.selector for r in records}) == len(records) before building the model."],"exampleFix":"# before\nrecords=[rec(selector='D', ...), rec(selector='D', ...)]  # duplicate -> ValueError\n\n# after\nrecords=[rec(selector='D1', ...), rec(selector='D2', ...)]","handlingStrategy":"validation","validationCode":"def selectors_unique(records) -> bool:\n    sel = [r.selector for r in records if r.selector is not None]\n    return len(set(sel)) == len(sel)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Assert selector uniqueness in layout unit tests.","Beware look-alike codes ('O' vs '0', 'l' vs '1') when hand-authoring layouts.","Generate layouts from the source system's record-type table to avoid duplicates."],"tags":["ebcdic","validation","pydantic","uniqueness"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}