Fastapi: [BUG] Serialization of models with SecretStr fails with `str type expected`

Created on 28 Nov 2019  路  4Comments  路  Source: tiangolo/fastapi

Describe the bug
When returning a model with SecretStr in it the response_model fails with str type expected

To Reproduce
Steps to reproduce the behavior:

from fastapi import FastAPI
from pydantic import BaseModel, SecretStr


class M(BaseModel):
    a: str
    b: SecretStr


app = FastAPI()


@app.post("/", response_model=M)
def post(m: M):

    return m

run with uvicorn app:app

and then:

curl -X POST "http://127.0.0.1:8000/" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"a\":\"s1\",\"b\":\"s2\"}"

which gives internal error and

or
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/uvicorn/protocols/http/httptools_impl.py", line 385, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/fastapi/applications.py", line 140, in __call__
    await super().__call__(scope, receive, send)
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/starlette/applications.py", line 134, in __call__
    await self.error_middleware(scope, receive, send)
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/starlette/middleware/errors.py", line 178, in __call__
    raise exc from None
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/starlette/middleware/errors.py", line 156, in __call__
    await self.app(scope, receive, _send)
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/starlette/exceptions.py", line 73, in __call__
    raise exc from None
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/starlette/exceptions.py", line 62, in __call__
    await self.app(scope, receive, sender)
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/starlette/routing.py", line 590, in __call__
    await route(scope, receive, send)
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/starlette/routing.py", line 208, in __call__
    await self.app(scope, receive, send)
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/starlette/routing.py", line 41, in app
    response = await func(request)
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/fastapi/routing.py", line 140, in app
    exclude_unset=response_model_exclude_unset,
  File "/home/sbv/.local/share/virtualenvs/repro/lib/python3.7/site-packages/fastapi/routing.py", line 72, in serialize_response
    raise ValidationError(errors, field.type_)
pydantic.error_wrappers.ValidationError: 1 validation error for M
response -> b
  str type expected (type=type_error.str)

Expected behavior
I would have expected it just to return the input back again but with * for secret string
{"a": "s1", "b": "***************"}

If it was return json.loads(m.json()) that is what would happen

Environment:

  • OS: Linux ubuntu
  • FastAPI Version: 0.44.0

    • Python version: 3.7.1

bug

Most helpful comment

I confirmed it; I'll raise an issue in pydantic.

All 4 comments

Looks like the pydantic model for a SecretStr is not idempotent. Can you confirm that, dropping FastAPI stuff completely, M(**dict(m)) raises the same error? If so, it should probably be fixed in pydantic.

For what it鈥檚 worth, this issue will probably be resolved separately once we can remove the secure cloned field for serialization (which I intend to work on now that pydantic v1 support is merged).

I confirmed it; I'll raise an issue in pydantic.

Thanks for the help here @dmontagu !

So, a SecretStr would be suitable for a model in a request, for example to send a password or security token, but the behavior would probably be more or less undefined for a response, as the thing is supposed to be a secret, but you are sending it back in a response in plain text.

In that case, it would be better to have a separate model for the response, with a standard str.

Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues.

Was this page helpful?
0 / 5 - 0 ratings