Skip to content

fix: handle field names with leading underscores in Pydantic model creation#219

Open
clawtom wants to merge 2 commits intoraw-labs:mainfrom
clawtom:fix/pydantic-underscore-field-names
Open

fix: handle field names with leading underscores in Pydantic model creation#219
clawtom wants to merge 2 commits intoraw-labs:mainfrom
clawtom:fix/pydantic-underscore-field-names

Conversation

@clawtom
Copy link
Copy Markdown

@clawtom clawtom commented Mar 12, 2026

Summary

Fixes #184.

Pydantic v2 treats field names starting with _ as private attributes and raises PydanticUserError ("Fields must not use names with leading underscores") when they appear in a create_model() call. This blocked users from declaring YAML return types with underscore-prefixed field names such as __url or __type.

Changes

In _create_pydantic_model_from_schema, when iterating over object properties:

  1. Detect underscore-prefixed names: if prop_name starts with _, generate a safe Python identifier ("field_" + prop_name.lstrip("_")) and set Field(alias=prop_name) so Pydantic maps the alias back to the original name during validation.
  2. Add populate_by_name=True to ConfigDict when aliased fields are present, allowing the model to accept both the alias (original YAML name) and the Python identifier.

No change to non-underscore field names — existing behavior is unaffected.

Example

Before this fix, the following YAML return type definition would raise a PydanticUserError:

return:
  type: object
  properties:
    __url:
      type: string
    __type:
      type: string

After this fix, validation works correctly: __url is accepted as an alias for the generated Python field field_url.


Note

Medium Risk
Touches dynamic Pydantic model generation used for endpoint parameter/return validation; mistakes could break validation or change accepted/returned field names for object schemas, though the change is narrowly scoped to underscore-prefixed properties.

Overview
Fixes generated Pydantic v2 models for JSON Schema object properties that start with _ by renaming them to safe Python identifiers (e.g., field_x) and preserving the original key via Field(alias=...).

When any such aliases are present, the generated model now enables populate_by_name so both the alias (original schema/YAML field) and the Python field name can be used during validation.

Written by Cursor Bugbot for commit 7bfb987. This will update automatically on new commits. Configure here.

…eation

Pydantic v2 rejects field names starting with `_`, treating them as private
attributes. When a YAML property name starts with `_`, generate a safe Python
identifier (`field_` + stripped name) and record the original via
`Field(alias=...)`. Add `populate_by_name=True` to ConfigDict so the model
accepts both the alias and the Python name during validation.

Fixes raw-labs#184
Comment thread src/mxcp/server/interfaces/server/mcp.py Outdated
Using lstrip("_") strips ALL leading underscores, so _url and __url
both map to "field_url" causing a silent overwrite. Use [1:] to strip
exactly one underscore, preserving the distinction.
@clawtom
Copy link
Copy Markdown
Author

clawtom commented Mar 13, 2026

Good catch from @cursor (bot). Fixed in the latest commit.

The issue: prop_name.lstrip("_") strips all leading underscores, so _url and __url both become "field_url", and the second silently overwrites the first in model_fields.

Fix: use prop_name[1:] to strip exactly one leading underscore:

  • _urlfield_url
  • __urlfield__url (distinct, no collision)

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

if prop_name.startswith("_"):
python_name = "field_" + prop_name[1:] # strip exactly one underscore to avoid name collisions
field_kwargs["alias"] = prop_name
has_aliased_fields = True
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silent field collision with field_ prefix mapping

Low Severity

When an underscore-prefixed property like _name is renamed to field_name, it can silently collide with an existing non-underscore property also called field_name in the same schema. Both would map to the same key in model_fields, causing one to silently overwrite the other and be dropped from the generated model. No collision detection or error is raised.

Fix in Cursor Fix in Web

@clawtom
Copy link
Copy Markdown
Author

clawtom commented Mar 13, 2026

The second Bugbot finding (field collision between _name and a literal field_name property) is a valid theoretical concern. In practice, having a parameter named field_name (with the field_ prefix) in the same MCP tool schema as _name is an unlikely combination — the field_ prefix is a Python-namespace artifact, not a natural YAML naming convention.

If it becomes a real concern, the fix would be to add collision detection before inserting into model_fields and raise a descriptive error, or to use a different prefix strategy (e.g. __ + name). Happy to add that if maintainers think the edge case warrants it.

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.

[BUG] .yaml type description forbids fields starting with underscore _

1 participant