-
Notifications
You must be signed in to change notification settings - Fork 16
Description
I am using the Miro Python client and getting a Pydantic model validation error when calling .get_boards()
I believe the issue is that the GET https://api.miro.com/v2/boards API endpoint returns the Picture.id field as a string representation of an integer, but in the Picture model, the id field is specified as either a StrictFloat or StrictInt, neither of which allow strings.
The Picture model id field is specified in packages/miro-api-python/miro_api/models/picture.py#L3 :
id: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Id of the picture")I can fix the error by modifying it to id: Optional[Union[StrictFloat, StrictInt, int]] to allow not-strict integers, but the project commit messages suggest it employs some kind of automated model generation so I assume this is not a suitable submission for a fix.
When calling the endpoint:
curl --request GET \
--url https://api.miro.com/v2/boards \
--header 'accept: application/json' \
--header 'authorization: Bearer XXXXXXXXXXXXXXXXXXXX'In the results I see picture id returned as a string:
"picture": {
"id": "3458764517504699683",
"type": "picture",
"imageURL": "https://mirostatic.com/board-image-assets/20250610/540x540/board_icon_13.png?etag=20250610"
},Simplest code to reproduce error (assumes one or more boards exist):
import os
import dotenv
import miro_api
dotenv.load_dotenv()
miro = miro_api.MiroApi(os.getenv("MIRO_ACCESS_TOKEN"))
boards = miro.get_boards()
for board in boards.data:
print(f"{board.id=} {board.name=}")Error message I receive:
Traceback (most recent call last):
File "/usr/lib/python3.12/runpy.py", line 198, in _run_module_as_main
return _run_code(code, main_globals, None,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/runpy.py", line 88, in _run_code
exec(code, run_globals)
File "/home/richbon/.vscode-server/extensions/ms-python.debugpy-2025.18.0-linux-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/__main__.py", line 71, in <module>
cli.main()
File "/home/richbon/.vscode-server/extensions/ms-python.debugpy-2025.18.0-linux-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 508, in main
run()
File "/home/richbon/.vscode-server/extensions/ms-python.debugpy-2025.18.0-linux-x64/bundled/libs/debugpy/adapter/../../debugpy/launcher/../../debugpy/../debugpy/server/cli.py", line 391, in run_module
run_module_as_main(options.target, alter_argv=True)
File "/home/richbon/.vscode-server/extensions/ms-python.debugpy-2025.18.0-linux-x64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 228, in _run_module_as_main
return _run_code(code, main_globals, None, "__main__", mod_spec)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/richbon/.vscode-server/extensions/ms-python.debugpy-2025.18.0-linux-x64/bundled/libs/debugpy/_vendored/pydevd/_pydevd_bundle/pydevd_runpy.py", line 118, in _run_code
exec(code, run_globals)
File "/home/richbon/coding/miro_test/src/main.py", line 9, in <module>
boards = miro.get_boards()
^^^^^^^^^^^^^^^^^
File "/home/richbon/coding/miro_test/.venv/lib/python3.12/site-packages/pydantic/_internal/_validate_call.py", line 39, in wrapper_function
return wrapper(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/richbon/coding/miro_test/.venv/lib/python3.12/site-packages/pydantic/_internal/_validate_call.py", line 136, in __call__
res = self.__pydantic_validator__.validate_python(pydantic_core.ArgsKwargs(args, kwargs))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/richbon/coding/miro_test/.venv/lib/python3.12/site-packages/miro_api/api/__init__.py", line 10589, in get_boards
return self.api_client.response_deserialize(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/richbon/coding/miro_test/.venv/lib/python3.12/site-packages/miro_api/api_client.py", line 282, in response_deserialize
return_data = self.deserialize(response_text, response_type)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/richbon/coding/miro_test/.venv/lib/python3.12/site-packages/miro_api/api_client.py", line 351, in deserialize
return self.__deserialize(data, response_type)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/richbon/coding/miro_test/.venv/lib/python3.12/site-packages/miro_api/api_client.py", line 394, in __deserialize
return self.__deserialize_model(data, klass)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/richbon/coding/miro_test/.venv/lib/python3.12/site-packages/miro_api/api_client.py", line 659, in __deserialize_model
return klass.from_dict(data)
^^^^^^^^^^^^^^^^^^^^^
File "/home/richbon/coding/miro_test/.venv/lib/python3.12/site-packages/miro_api/models/boards_paged_response.py", line 125, in from_dict
"data": [Board.from_dict(_item) for _item in obj["data"]] if obj.get("data") is not None else None,
^^^^^^^^^^^^^^^^^^^^^^
File "/home/richbon/coding/miro_test/.venv/lib/python3.12/site-packages/miro_api/models/board.py", line 193, in from_dict
"picture": Picture.from_dict(obj["picture"]) if obj.get("picture") is not None else None,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/richbon/coding/miro_test/.venv/lib/python3.12/site-packages/miro_api/models/picture.py", line 98, in from_dict
_obj = cls.model_validate(
^^^^^^^^^^^^^^^^^^^
File "/home/richbon/coding/miro_test/.venv/lib/python3.12/site-packages/pydantic/main.py", line 716, in model_validate
return cls.__pydantic_validator__.validate_python(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.ValidationError: 2 validation errors for Picture
id.float
Input should be a valid number [type=float_type, input_value='3458764517504699683', input_type=str]
For further information visit https://errors.pydantic.dev/2.12/v/float_type
id.int
Input should be a valid integer [type=int_type, input_value='3458764517504699683', input_type=str]
For further information visit https://errors.pydantic.dev/2.12/v/int_type
I am running Python 3.12.3 and this is the pip freeze output of my virtual environment, set up today:
annotated-types==0.7.0
black==25.12.0
click==8.3.1
miro_api==2.2.4
mypy_extensions==1.1.0
packaging==25.0
pathspec==0.12.1
platformdirs==4.5.1
pydantic==2.12.5
pydantic_core==2.41.5
python-dateutil==2.9.0.post0
python-dotenv==1.2.1
pytokens==0.3.0
six==1.17.0
typing-inspection==0.4.2
typing_extensions==4.15.0
urllib3==1.26.20