Skip to content

AssemblerError / TypeError when processing extension slices on primitive-type fields #336

@luisfabib

Description

@luisfabib

When a StructureDefinition differential defines slices on the extension of a primitive-type field (e.g., Observation.status.extension:mySlice), FHIRModelFactory.build() raises an AssemblerError wrapping a TypeError:

TypeError: TypeAliasType.__init__() got an unexpected keyword argument '__pydantic_reset_parent_namespace__'

This is because BackboneFieldBuilder incorrectly claims ownership of the primitive element (e.g., Observation.status) once its extension slice children appear in the resolved index — then attempts to use the primitive's TypeAliasType (e.g., ObservationStatus) as a Pydantic __base__, which Pydantic rejects.

Steps to Reproduce

from fhircraft.fhir.resources import FHIRModelFactory
from fhircraft.fhir.resources.generator import CodeGenerator

factory = FHIRModelFactory(fhir_release="R5")

structure_definition_1 = {
    "resourceType": "StructureDefinition",
    "id": "example-profile-1",
    "url": "http://hl7.org/fhir/StructureDefinition/example-profile-1",
    "version": "5.0.0",
    "name": "ExampleProfile1",
    "title": "Example Profile 1",
    "status": "draft",
    "fhirVersion": "5.0.0",
    "kind": "resource",
    "abstract": False,
    "type": "Observation",
    "baseDefinition": "http://hl7.org/fhir/StructureDefinition/Observation",
    "derivation": "constraint",
    "differential": {
        "element": [
            {"id": "Observation.status", "path": "Observation.status"},
            {"id": "Observation.status.extension", "path": "Observation.status.extension"},
            {
                "id": "Observation.status.extension:mySlice",
                "path": "Observation.status.extension",
                "sliceName": "mySlice",
                "type": [{
                    "code": "Extension",
                    "profile": ["http://example.org/fhir/StructureDefinition/my-extension"]
                }],
            }
        ]
    },
}

extension_structure_definition_1 = {
    "resourceType": "StructureDefinition",
    "id": "my-extension",
    "url": "http://example.org/fhir/StructureDefinition/my-extension",
    "version": "0.1.0",
    "name": "MyExtension",
    "status": "active",
    "fhirVersion": "5.0.0",
    "kind": "complex-type",
    "abstract": False,
    "type": "Extension",
    "baseDefinition": "http://hl7.org/fhir/StructureDefinition/Extension",
    "derivation": "constraint",
    "differential": {
        "element": [
            {"id": "Extension.url", "path": "Extension.url",
             "fixedUri": "http://example.org/fhir/StructureDefinition/my-extension"},
            {"id": "Extension.value[x]", "path": "Extension.value[x]",
             "type": [{"code": "string"}], "min": 1, "max": "1"}
        ]
    },
}

factory.definition_registry.from_dict(structure_definition_1)
factory.definition_registry.from_dict(extension_structure_definition_1)

# Raises AssemblerError
model = factory.build(structure_definition=structure_definition_1, mode="differential")

Full Traceback

File ~/dev/fhircraft/fhircraft/fhir/resources/factory/assembler.py:113, in ModelAssembler.assemble
    build = builder.build(child_node, self.index)

File ~/dev/fhircraft/fhircraft/fhir/resources/factory/builders/backbone.py:94, in BackboneFieldBuilder.build
    backbone_model = assembler.assemble(backbone_name, base=(backbone_base,))

File ~/dev/fhircraft/fhircraft/fhir/resources/factory/assembler.py:167, in ModelAssembler.assemble
    model = create_model(name, **fields, __base__=base_classes, ...)

  File .../pydantic/main.py:1809, in create_model
    return meta(model_name, resolved_bases, namespace, ...)

TypeError: TypeAliasType.__init__() got an unexpected keyword argument '__pydantic_reset_parent_namespace__'

AssemblerError: Builder failed for element 'Observation.status':
  TypeAliasType.__init__() got an unexpected keyword argument '__pydantic_reset_parent_namespace__'

Root Cause Analysis

The bug has two interrelated causes:

1. BackboneFieldBuilder.can_handle() fires on primitive fields with extension children

In backbone.py, can_handle() returns True whenever index.get_children(node.id) is non-empty:

def can_handle(self, node: ElementNode, index: DefinitionIndex) -> bool:
    has_children = bool(index.get_children(node.id))
    if has_children:
        return True  # ← fires for Observation.status when it has .extension children
    ...

When the differential contains Observation.status.extension and its slices, those elements are stored as children of Observation.status in the DefinitionIndex. This causes BackboneFieldBuilder to claim ownership of Observation.status, even though it is a primitive type (FHIR code), not a BackboneElement.

2. Primitive type's TypeAliasType is passed as a Pydantic __base__

BackboneFieldBuilder.build() then looks up backbone_base from the parent model's field annotation. For a primitive field like status, the annotation resolves to a TypeAliasType (e.g., ObservationStatus). When this is handed to pydantic.create_model(..., __base__=(TypeAliasType,)), Pydantic fails with the TypeError above.

3. Missing _ext placeholder awareness in the resolver/builder

Per the FHIR JSON serialization spec, extensions on primitive-type fields are serialized under the _<fieldname> sibling element (represented internally as <fieldname>_ext). The resolver and builder currently do not detect when Observation.status.extension children actually belong to the primitive extension placeholder (status_ext: Element) rather than the field itself. This accounting is already handled correctly for snapshot-derived models via build_primitive_extension_placeholder() in base.py, but it is not applied when processing differential elements that introduce extension slices on primitive fields.

Expected Behaviour

factory.build() should succeed and the generated model should:

  • Keep status as a primitive-type field (annotated with ObservationStatus / the appropriate code type).
  • Emit a status_ext / _status companion field (of type Element or a constrained subtype) that carries the extension slice information — consistent with the FHIR primitive extension mechanism already implemented in SimpleFieldBuilder and build_primitive_extension_placeholder().

Actual Behaviour

factory.build() raises:

AssemblerError: Builder failed for element 'Observation.status':
  TypeAliasType.__init__() got an unexpected keyword argument '__pydantic_reset_parent_namespace__'

Likely Fix

  • BackboneFieldBuilder.can_handle() should guard against primitive-type nodes even when they have children. A node whose resolved FHIR kind is "primitive" (or whose annotation in the base model is not a subclass of BaseModel) should not be claimed by this builder.
  • The differential resolver / assembler should detect when children of a primitive field are exclusively extension/slice elements and, instead of building a backbone sub-model, route them through the existing build_primitive_extension_placeholder() path so the _ext companion field is enriched with the slice constraints.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions