Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] pydantic.errors.PydanticInvalidForJsonSchema occured when APIRouter's response_model has PydanticObjectId Field #1087

Closed
hagd0520 opened this issue Dec 11, 2024 · 12 comments · May be fixed by #1099

Comments

@hagd0520
Copy link

Describe the bug
pydantic.errors.PydanticInvalidForJsonSchema occured when APIRouter's response_model has PydanticObjectId Field
It occurs to beanie 1.28

To Reproduce

    @sample_router.post("$", response_model=PydanticObjectId)
    async def create_sample():
        sample = await self.sample_service.create()
        return sample.id

Stack trace

Traceback (most recent call last):
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 401, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        self.scope, self.receive, self.send
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\uvicorn\middleware\proxy_headers.py", line 70, in __call__
    return await self.app(scope, receive, send)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\fastapi\applications.py", line 1054, in __call__
    await super().__call__(scope, receive, send)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\applications.py", line 123, in __call__
    await self.middleware_stack(scope, receive, send)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\middleware\errors.py", line 186, in __call__
    raise exc
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\middleware\errors.py", line 164, in __call__
    await self.app(scope, receive, _send)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\middleware\cors.py", line 85, in __call__
    await self.app(scope, receive, send)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\middleware\exceptions.py", line 65, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\routing.py", line 756, in __call__
    await self.middleware_stack(scope, receive, send)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\routing.py", line 776, in app
    await route.handle(scope, receive, send)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\routing.py", line 297, in handle
    await self.app(scope, receive, send)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\routing.py", line 77, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\starlette\routing.py", line 72, in app
    response = await func(request)
               ^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\fastapi\applications.py", line 1009, in openapi
    return JSONResponse(self.openapi())
                        ~~~~~~~~~~~~^^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\fastapi\applications.py", line 981, in openapi
    self.openapi_schema = get_openapi(
                          ~~~~~~~~~~~^
        title=self.title,
        ^^^^^^^^^^^^^^^^^
    ...<11 lines>...
        separate_input_output_schemas=self.separate_input_output_schemas,
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\fastapi\openapi\utils.py", line 475, in get_openapi
    field_mapping, definitions = get_definitions(
                                 ~~~~~~~~~~~~~~~^
        fields=all_fields,
        ^^^^^^^^^^^^^^^^^^
    ...<2 lines>...
        separate_input_output_schemas=separate_input_output_schemas,
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\fastapi\_compat.py", line 229, in get_definitions
    field_mapping, definitions = schema_generator.generate_definitions(
                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        inputs=inputs
        ^^^^^^^^^^^^^
    )
    ^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\pydantic\json_schema.py", line 379, in generate_definitions
    self.generate_inner(schema)
    ~~~~~~~~~~~~~~~~~~~^^^^^^^^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\pydantic\json_schema.py", line 552, in generate_inner
    json_schema = current_handler(schema)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\pydantic\_internal\_schema_generation_shared.py", line 37, in __call__
    return self.handler(core_schema)
           ~~~~~~~~~~~~^^^^^^^^^^^^^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\pydantic\json_schema.py", line 527, in new_handler_func
    json_schema = js_modify_function(schema_or_field, current_handler)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\beanie\odm\fields.py", line 178, in __get_pydantic_json_schema__
    json_schema = handler(schema)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\pydantic\_internal\_schema_generation_shared.py", line 37, in __call__
    return self.handler(core_schema)
           ~~~~~~~~~~~~^^^^^^^^^^^^^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\pydantic\json_schema.py", line 511, in handler_func
    json_schema = generate_for_schema_type(schema_or_field)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\pydantic\json_schema.py", line 1263, in json_or_python_schema
    return self.generate_inner(schema['json_schema'])
           ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\pydantic\json_schema.py", line 552, in generate_inner
    json_schema = current_handler(schema)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\pydantic\_internal\_schema_generation_shared.py", line 37, in __call__
    return self.handler(core_schema)
           ~~~~~~~~~~~~^^^^^^^^^^^^^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\pydantic\json_schema.py", line 511, in handler_func
    json_schema = generate_for_schema_type(schema_or_field)
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\pydantic\json_schema.py", line 1014, in function_plain_schema
    return self.handle_invalid_for_json_schema(
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
        schema, f'core_schema.PlainValidatorFunctionSchema ({schema["function"]})'
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "C:\Users\hagd0\AppData\Local\pypoetry\Cache\virtualenvs\ceep-api-B8fTZraC-py3.13\Lib\site-packages\pydantic\json_schema.py", line 2185, in handle_invalid_for_json_schema
    raise PydanticInvalidForJsonSchema(f'Cannot generate a JsonSchema for {error_info}')
pydantic.errors.PydanticInvalidForJsonSchema: Cannot generate a JsonSchema for core_schema.PlainValidatorFunctionSchema ({'type': 'with-info', 'function': <bound method PydanticObjectId.validate of <class 'beanie.odm.fields.PydanticObjectId'>>})
@staticxterm
Copy link
Contributor

Hi @hagd0520, thank you for the report.
This issue will be solved by #1080.

@charlesdzadu
Copy link

Hi Guys. I'm facing the same issue.

@hagd0520 hagd0520 reopened this Dec 17, 2024
@microyahoo
Copy link

I met same issue too.

1 similar comment
@RitinaADM
Copy link

I met same issue too.

@pharmac1st
Copy link

pharmac1st commented Dec 29, 2024

Just hit this as well

pydantic.errors.PydanticInvalidForJsonSchema: Cannot generate a JsonSchema for core_schema.PlainValidatorFunctionSchema ({'type': 'with-info', 'function': <bound method PydanticObjectId.validate of <class 'beanie.odm.fields.PydanticObjectId'>>})

For further information visit https://errors.pydantic.dev/2.10/u/invalid-for-json-schema

Using the latest fix seems to help pydantic BaseModel attribute definitions of PydanticObjectId, but I still needed to remove the FastAPI argument definitons, ie:

# Doesn't work
@router.post("/blah/{foo_id}/")
async def my_route(
    body: ..., foo_id: PydanticObjectId
):

# Works
@router.post("/blah/{foo_id}")
async def my_route(
    body: ..., foo_id: str
):

@staticxterm
Copy link
Contributor

Hi @pharmac1st,

could you please provide some more information about your environment, like which Pydantic version are you using?
If using v1, this may still be an issue and Roman himself suggests doing something like this (as FastAPI defines their own encoders, which are outside of Beanie's control and can't hook into them the same way as it does with Pydantic): #621 (comment)

@a3963969
Copy link

a3963969 commented Jan 2, 2025

fix: resolve PydanticObjectId JSON Schema generation issue

Problem:

  • FastAPI's OpenAPI schema generation fails with PydanticObjectId fields
  • Error: "Cannot generate a JsonSchema for core_schema.PlainValidatorFunctionSchema"
  • This is due to compatibility issues between Beanie ODM and Pydantic v2

Solution:

  • Replace PydanticObjectId with str type for MongoDB _id fields
  • Use Field with alias="_id" to maintain MongoDB compatibility
  • Example: id: Optional[str] = Field(default=None, alias="_id")

@dantetemplar
Copy link
Contributor

fix: resolve PydanticObjectId JSON Schema generation issue

Problem:

* FastAPI's OpenAPI schema generation fails with PydanticObjectId fields

* Error: "Cannot generate a JsonSchema for core_schema.PlainValidatorFunctionSchema"

* This is due to compatibility issues between Beanie ODM and Pydantic v2

Solution:

* Replace PydanticObjectId with str type for MongoDB _id fields

* Use Field with alias="_id" to maintain MongoDB compatibility

* Example: id: Optional[str] = Field(default=None, alias="_id")

That is not the wanted solution as it will not show "that field actually is bson ObjectId" on client side. I guess we need fix...

@dantetemplar
Copy link
Contributor

Hi @pharmac1st,

could you please provide some more information about your environment, like which Pydantic version are you using? If using v1, this may still be an issue and Roman himself suggests doing something like this (as FastAPI defines their own encoders, which are outside of Beanie's control and can't hook into them the same way as it does with Pydantic): #621 (comment)

It can't be related to encoders as it is occurs during mode="validation" step (route arguments).

@dantetemplar
Copy link
Contributor

dantetemplar commented Jan 2, 2025

Issue seems to be related to that commit: 66d78bf

@dantetemplar
Copy link
Contributor

I found workaround, plz check it 🐍

@hagd0520
Copy link
Author

hagd0520 commented Jan 8, 2025

This prob is solved from 1.29.0 ver. tyvm.

@hagd0520 hagd0520 closed this as completed Jan 8, 2025
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 a pull request may close this issue.

8 participants