Skip to content

fern-python-sdk produces broken code when extending circular types across file boundaries #14866

@charlesdaniels

Description

@charlesdaniels

Component

SDKs

Priority

P0 - Critical (Blocking work)

SDK Language(s)

Python

Bug Description

I believe this is a regression, as it works fine in 4.18.6, but does not work in any version I tested beginning with 4.19.0. In 4.32.0, the problem became worse.

When extends is used to inherit from a type defined in a different file, and the base type is part of a circular reference (within its own file), the generator misplaces, and in later version entirely elides import statements that are required for the Python code to be loaded, resulting in a NameError exception when the Python library is imported.

Consider the following Fern definition:

fern/definition/api.yml

name: repro

fern/definition/common.yml

types:
  # BaseType references ChildType, and ChildType extends BaseType,
  # creating a circular reference cycle.
  BaseType:
    properties:
      child_ref: optional<ChildType>

  ChildType:
    extends: BaseType
    properties:
      child_name: string

fern/definition/derived.yml

imports:
  common: common.yml

types:
  # DerivedType extends BaseType from another file.
  # BaseType is in a circular reference cycle (BaseType <-> ChildType).
  # Bug: fern-python-sdk 5.1.2 generates `class DerivedType(BaseType):` in
  # derived/types/derived_type.py without importing BaseType.
  DerivedType:
    extends: common.BaseType
    properties:
      derived_name: string

There should be imports in the generated derived_type.py. Beginning in 4.19.0, they moved to the bottom of the file...

--- fern-python-repro-test-4.18.6/repro/derived/types/derived_type.py	2026-04-09 17:45:32
+++ fern-python-repro-test-4.19.0/repro/derived/types/derived_type.py	2026-04-09 17:45:40
@@ -1,9 +1,10 @@
 # This file was auto-generated by Fern from our API Definition.

+from __future__ import annotations
+
 import typing

 import pydantic
-from ...common.types.base_type import BaseType
 from ...core.pydantic_utilities import IS_PYDANTIC_V2


@@ -17,3 +18,7 @@
         class Config:
             smart_union = True
             extra = pydantic.Extra.allow
+
+
+from ...common.types.base_type import BaseType  # noqa: E402, F401, I001
+from ...common.types.child_type import ChildType  # noqa: E402, F401, I001

This breaks when trying to import the library:

$  python3 -c "import sys; sys.path.insert(0, 'fern-python-repro-test-4.18.6'); import repro.derived.types.derived_type"
/Users/cad/f/tmp/2026-04-09_fern-python-repro/fern-python-repro-test-4.18.6/repro/core/pydantic_utilities.py:13: UserWarning: Core Pydantic V1 functionality isn't compatible with Python 3.14 or greater.
  from pydantic.v1.datetime_parse import parse_date as parse_date
/Users/cad/.local/dotsenv/lib/python3.14/site-packages/pydantic/_internal/_config.py:383: UserWarning: Valid config keys have changed in V2:
* 'allow_population_by_field_name' has been renamed to 'validate_by_name'
* 'smart_union' has been removed
  warnings.warn(message, UserWarning)
$  python3 -c "import sys; sys.path.insert(0, 'fern-python-repro-test-4.19.0'); import repro.derived.types.derived_type"
/Users/cad/f/tmp/2026-04-09_fern-python-repro/fern-python-repro-test-4.19.0/repro/core/pydantic_utilities.py:13: UserWarning: Core Pydantic V1 functionality isn't compatible with Python 3.14 or greater.
  from pydantic.v1.datetime_parse import parse_date as parse_date
/Users/cad/.local/dotsenv/lib/python3.14/site-packages/pydantic/_internal/_config.py:383: UserWarning: Valid config keys have changed in V2:
* 'allow_population_by_field_name' has been renamed to 'validate_by_name'
* 'smart_union' has been removed
  warnings.warn(message, UserWarning)
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import sys; sys.path.insert(0, 'fern-python-repro-test-4.19.0'); import repro.derived.types.derived_type
                                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cad/f/tmp/2026-04-09_fern-python-repro/fern-python-repro-test-4.19.0/repro/__init__.py", line 5, in <module>
    from . import common, derived
  File "/Users/cad/f/tmp/2026-04-09_fern-python-repro/fern-python-repro-test-4.19.0/repro/derived/__init__.py", line 5, in <module>
    from .types import DerivedType
  File "/Users/cad/f/tmp/2026-04-09_fern-python-repro/fern-python-repro-test-4.19.0/repro/derived/types/__init__.py", line 5, in <module>
    from .derived_type import DerivedType
  File "/Users/cad/f/tmp/2026-04-09_fern-python-repro/fern-python-repro-test-4.19.0/repro/derived/types/derived_type.py", line 11, in <module>
    class DerivedType(BaseType):
                      ^^^^^^^^
NameError: name 'BaseType' is not defined

This problem was made worse in 4.32.0, because the imports were removed entirely.

--- fern-python-repro-test-4.18.6/repro/derived/types/derived_type.py	2026-04-09 17:45:32
+++ fern-python-repro-test-4.32.0/repro/derived/types/derived_type.py	2026-04-09 17:45:49
@@ -1,9 +1,10 @@
 # This file was auto-generated by Fern from our API Definition.

+from __future__ import annotations
+
 import typing

 import pydantic
-from ...common.types.base_type import BaseType
 from ...core.pydantic_utilities import IS_PYDANTIC_V2

This is still broken as of the latest 5.3.6 release:

$  python3 -c "import sys; sys.path.insert(0, 'fern-python-repro-test-5.3.6'); import repro.derived.types.derived_type"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import sys; sys.path.insert(0, 'fern-python-repro-test-5.3.6'); import repro.derived.types.derived_type
                                                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/cad/f/tmp/2026-04-09_fern-python-repro/fern-python-repro-test-5.3.6/repro/derived/types/derived_type.py", line 11, in <module>
    class DerivedType(BaseType):
                      ^^^^^^^^
NameError: name 'BaseType' is not defined

I will include a tarball with the generated outputs for 4.18.6, 4.19.0, 4.32.0, and 5.3.6, along with the Fern definitions and the script I used to generate the outputs.

My Python is a little rusty but I'm open to help out with this if I can.

fern-sdk-repro.tgz

Versions

CLI version 4.47.0, python SDK generator 4.19.0 .. 5.3.6.

Workaround

This blocks upgrading the Python SDK generator past 4.19.0 for any Fern definition that triggers this bug.

Are you interested in contributing a fix?

Yes

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions