Skip to content

fix(signatures): raise clear error for field names that shadow SignatureMeta properties - #10242

Open
STiFLeR7 wants to merge 1 commit into
stanfordnlp:mainfrom
STiFLeR7:fix/signature-reserved-field-name
Open

STiFLeR7 wants to merge 1 commit into
stanfordnlp:mainfrom
STiFLeR7:fix/signature-reserved-field-name

Conversation

@STiFLeR7

Copy link
Copy Markdown

📝 Changes Description

Declaring a Signature field whose name collides with a SignatureMeta property (e.g. instructions, the metaclass property used to read/write the docstring) crashes signature class construction with an opaque internal error instead of a clear validation message:

class MySig(dspy.Signature):
    instructions: str = dspy.InputField()
    answer: str = dspy.OutputField()
# TypeError: 'NoneType' object is not subscriptable

Root cause: Pydantic detects the name collision itself (it emits its own UserWarning: Field name "instructions" ... shadows an attribute in parent "Signature") and, for that field only, produces a plain FieldInfo with json_schema_extra=None instead of preserving the InputField()/OutputField() instance from the class body. SignatureMeta already has validation for exactly this shape of problem — _validate_fields() raises a clear TypeError when a field's json_schema_extra doesn't carry the expected __dspy_field_type key — but it ran too late in __new__: when no docstring is given, _default_instructions(cls) runs first, which calls cls.input_fields → _get_fields_with_type(), which indexes json_schema_extra directly and crashes before _validate_fields() ever gets a chance to run.

Fix: move the existing _validate_fields() call to run immediately after Pydantic builds the class, before anything reads json_schema_extra. No new validation logic — just correct ordering of what was already there. After the fix:

TypeError: Field `instructions` in `MySig` must be declared with InputField or OutputField, but field `instructions` has `field.json_schema_extra=None`

Fixes #1710.

✅ Contributor Checklist

  • Pre-Commit checks are passing (locally and remotely)
  • Title of your PR / MR corresponds to the required format
  • Commit message follows required format fix(signatures): ...

⚠️ Warnings

None — this only changes the order of two pre-existing steps in SignatureMeta.__new__; no new validation logic was added, so behavior for non-colliding field names (the common case) is unchanged.

Testing

  • Reverted the fix locally and reproduced the exact reported TypeError: 'NoneType' object is not subscriptable before reapplying it.
  • Added a regression test asserting the clear _validate_fields error is now raised instead.
  • uv run pytest tests/signatures/ tests/primitives/ -q: 172 passed (6 deselected pre-existing Windows temp-file-permission failures, confirmed present on unmodified main too and unrelated to signature construction).
  • uv run pytest tests/ -q (full suite, minus pre-existing environment failures — see companion PR fix(primitives): prevent named_parameters()/named_sub_modules() dict-key collisions #10241 for the same three confirmed-pre-existing exclusions): all passing.

…ureMeta properties

Declaring a Signature field whose name collides with a SignatureMeta
property (e.g. `instructions`, which is a class-level property on the
metaclass for reading/writing the docstring) crashed signature class
construction with an opaque internal error:

    class MySig(dspy.Signature):
        instructions: str = dspy.InputField()
        answer: str = dspy.OutputField()
    # TypeError: 'NoneType' object is not subscriptable

Pydantic detects the name collision (it emits its own "shadows an
attribute in parent" UserWarning) and, for that field only, produces a
plain FieldInfo with json_schema_extra=None instead of preserving the
InputField()/OutputField() instance from the class body. SignatureMeta
already has validation for exactly this shape of problem -
_validate_fields() raises a clear TypeError when a field's
json_schema_extra doesn't carry the expected __dspy_field_type key -
but it ran too late: SignatureMeta.__new__ computes default
instructions first when no docstring is given, via
_default_instructions(cls) -> cls.input_fields ->
_get_fields_with_type(), which indexes json_schema_extra directly and
crashes before _validate_fields() ever gets a chance to run.

Move the existing _validate_fields() call to run immediately after
Pydantic builds the class, before anything reads json_schema_extra.
No new validation logic - just correct ordering of what was already
there.

Fixes stanfordnlp#1710.
@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This change validates DSPy Signature fields immediately after Pydantic creates the class, preventing metadata access from obscuring malformed field declarations. The focused before-and-after check confirmed that normal Signature construction, field classification, generated instructions, and serialization still work, while an instructions field collision now raises the intended clear field-declaration error instead of an internal NoneType exception.

T-Rex validation blocked

The focused repository pytest command could not collect because the litellm package is missing from the environment. The independently executed harness exercised the modified class-construction paths successfully.

Confidence Score: 5/5

Safe to merge based on the exercised Signature construction behavior.

The before-and-after harness directly exercised the changed metaclass path: valid Signatures remained usable, and the property-shadowing failure now produces the intended validation error. No defects remain to report.

Files Needing Attention: No additional files need attention; the focused implementation path in dspy/signatures/signature.py and its regression scenario were exercised.

T-Rex T-Rex Logs

What T-Rex did

  • Ran the pre-PR focused signature validation script; it showed a valid Signature construction and that the collision produced a prior NoneType error.
  • Ran the PR revision of the signature validation script; the process exited successfully, the Signature construction remained valid, and the collision raised a Field 'instructions' error that must be declared with InputField or OutputField.
  • Attempted targeted pytest collection for signature tests, but collection was blocked by a missing environment dependency (litellm); the independent focused harness executed the changed code path successfully.
  • Uploaded and organized supporting artifacts to enable review, including the focused signature validation script and the run outputs from pre-PR and PR executions.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "fix(signatures): raise clear error for f..." | Re-trigger Greptile

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DSPy signature reserved keywords

1 participant