Skip to content

Python 3.14: unquoted forward references fail during Struct class creation #1165

Closed

Description

@stimyd

Description

### Description

On Python 3.14, an unquoted forward reference in a `msgspec.Struct`
field raises `NameError` while the Struct class is being created.

Python 3.14's default deferred-annotation behavior normally permits this
without quoting. The equivalent example using ordinary classes works.

Tested with:

- CPython 3.14.0
- msgspec 0.21.1 (currently the latest release on PyPI)

### Minimal reproducer

```python
import msgspec


class Outer(msgspec.Struct):
    inner: Inner


class Inner(msgspec.Struct):
    value: int

Actual result

Traceback (most recent call last):
  File "<string>", line 6, in <module>
    class Outer(msgspec.Struct):
        inner: Inner
  File "<string>", line 7, in __annotate__
    inner: Inner
           ^^^^^
NameError: name 'Inner' is not defined

Expected result

I expected Outer to be created successfully, with Inner resolved after
the module finishes defining it, as happens with an ordinary Python 3.14
class:

class Outer:
    inner: Inner


class Inner:
    value: int


assert Outer.__annotations__ == {"inner": Inner}

Workarounds

Either of the following prevents the error:

class Outer(msgspec.Struct):
    inner: "Inner"

or:

from __future__ import annotations

Both workarounds use stringified annotations instead of Python 3.14's
default deferred-annotation behavior.

Additional context

This appears related to:

PR #852 added Python 3.14 annotation support, but Struct field collection
currently invokes the class annotate function with
annotationlib.Format.VALUE:

https://github.com/msgspec/msgspec/blob/main/src/msgspec/_core.c#L6110-L6133

Python's annotationlib documentation recommends that metaclasses
inspecting annotations during class construction generally use
Format.FORWARDREF, because names may not yet be resolvable:

https://docs.python.org/3.14/library/annotationlib.html#using-annotations-in-a-metaclass

Would using Format.FORWARDREF, followed by the existing delayed
forward-reference resolution, be appropriate here?

A practical consequence is that Ruff's UP037 rule considers the explicit
quotes unnecessary on Python 3.14, but removing them can make modules
containing msgspec.Struct schemas fail to import.

Activity

  1. added a commit that references this issue on Sep 5, 2026
    7c2f473
  2. zyv commented on Sep 13, 2026

    @zyv

    I've also just hit this bug, and it's awesome that it's already fixed in the tree. Thanks @vyrnsynx for working on this. Looking forward to the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions