fastapi: FastAPI 0.65.2 POST request fails with "value is not a valid dict" when using the Requests library; 0.65.1 works (with a caveat)
First check
- I added a very descriptive title to this issue.
- I used the GitHub search to find a similar issue and didn’t find it.
- I searched the FastAPI documentation, with the integrated search.
- I already searched in Google “How to X in FastAPI” and didn’t find any information.
- I already read and followed all the tutorial in the docs and didn’t find an answer.
- I already checked if it is not related to FastAPI but to Pydantic.
- I already checked if it is not related to FastAPI but to Swagger UI.
- I already checked if it is not related to FastAPI but to ReDoc.
- After submitting this, I commit to one of:
- Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
- I already hit the “watch” button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
- Implement a Pull Request for a confirmed bug.
First off, thank you for creating this marvellous piece of software. It is a real life-changer.
I hit a very odd bug while implementing some unit tests. Using FastAPI 0.65.2, a POST request via the requests module (requests.post) consistently returns the following error:
{'detail': [{'loc': ['body'], 'msg': 'value is not a valid dict', 'type': 'type_error.dict'}]}
I created a reproducible example:
from fastapi import FastAPI
from pydantic import BaseModel
import uvicorn
app = FastAPI()
class Data(BaseModel):
field: str
@app.post("/test")
def test_data(data: Data):
return "Polo"
if __name__=='__main__':
uvicorn.run(app)
And the requests counterpart:
import requests as rq
def test():
result = rq.post('http://127.0.0.1:8000/test', data={"field": "Marco"})
print(f"==[ result: {result.json()}")
if __name__=="__main__":
test()
The result is
==[ result: {'detail': [{'loc': ['body'], 'msg': 'value is not a valid dict', 'type': 'type_error.dict'}]}
If I downgrade FastAPI to 0.65.1, I still get an error, but a different one:
==[ result: {'detail': [{'loc': ['body', 0], 'msg': 'Expecting value: line 1 column 1 (char 0)', 'type': 'value_error.jsondecode', 'ctx': {'msg': 'Expecting value', 'doc': 'field=heya', 'pos': 0, 'lineno': 1, 'colno': 1}}]}
This can be solved by JSONifying the dictionary:
import requests as rq
import json as js
def test():
result = rq.post('http://127.0.0.1:8000/test', data = js.dumps({"field": "Marco"}))
print(f"==[ result: {result.json()}")
if __name__=="__main__":
test()
Running the above prints
==[ result: Polo
I am slightly perplexed because I am not sure if it is the requests library that is to blame or FastAPI. POSTing via ReDoc works without hiccups with both versions. I am at a loss as to why the second parameter to requests.post is accepted as JSON when it should actually be a dictionary.
Please let me know if I’m missing something obvious or whether I should redirect this to the maintainers of requests. Thank you again for making the lives of developers so much easier.
Cheers!
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 26 (8 by maintainers)
You probably meant to use the json parameter, not the data parameter. Data is for form data
Can confirm, this still happens! We solved it by adding a
-H "Content-Type: application/json"to the curlI think this issue is critical, I saw this on 0.70.0 too, make a POST request from httpx with json=msg, headers={“Content-Type”: “application/json; charset=utf-8”} does not resolve this issue.
my pydantic model just simple like this:
when I test the json string in /docs, it work, but post it via httpx/requests it will return error: {‘detail’: [{‘loc’: [‘body’], ‘msg’: ‘value is not a valid dict’, ‘type’: ‘type_error.dict’}]}
New update: found the bug, when setup with pydantic model, fastapi expect request_body to be received as dict, not string, if you send request and feed the json string into json param like this:
it will generate error {‘detail’: [{‘loc’: [‘body’], ‘msg’: ‘value is not a valid dict’, ‘type’: ‘type_error.dict’}]} my FastAPI version is 0.70.0. if you setup the path operation with pydantic model, remember to send request as dict, not json, like this:
@tiangolo is this normal behavior ?
I got this error too. My use case was sending a base64 encoded image.
After trying a mix of the solutions above, I ended up with the below.
Weirdly enough, Postman works despite not setting the
charset.I just tried v0.67.0 and I’m still seeing the issue. Taking the initial example, with the following request:
This doesn’t work (shows
INFO: 127.0.0.1:35400 - "POST /test HTTP/1.1" 422 Unprocessable Entity):And this works fine (prints
{'field': 'Marco'}):The reason the downgrade fixed it was due to the json post handling in the latest version - it was changed so it only tried to parse bodies with a valid Content-Type header as json, which the data parameter wouldn’t set
AWS SNS HTTP/HTTPS notification JSON format messages specify content-type of
text/plain; charset=UTF-8. This causes the current version of FastAPI (0.68.1) to fail with a 422 exception discussed above, which appears to be avalue is not a valid dict (type=type_error.dict)400 exception when overriding the RequestValidationError and logging theexcas a string. Downgrading to 0.65.1 resolved the issue.Example AWS SNS JSON message from https://docs.aws.amazon.com/sns/latest/dg/sns-message-and-json-formats.html#http-notification-json
@Mause
Thank you for the quick reply. What you suggested indeed fixed the problem, so I will mark this as resolved, but I am still perplexed. I never knew that the
dataparameter was for form data - I have always useddatauntil I encountered this issue. This is from therequestsdocumentation:Also, I’m not sure why downgrading FastAPI works (subject to the JSONification caveat above).
Still, all is well that ends well. 😃 Thank you for your help!
Also, using
requests.post("/create-user", json={"some": "data"})requires the value passed tojsonto be the Python objects because Requests serializes that to JSON bytes (a string). If you serialize it before and pass it a string, then Requests will send it as a JSON string data type, not the pure data serialized as JSON.It’s not related to FastAPI, FastAPI expects standard JSON. It’s related to how Requests expects the data in the parameter and how it serializes automatically for you.
This is a rather major change of behaviour!
It broke plenty of existing integrations at $work 😦
I just updated from 0.63 to 0.78 and I had the error too.
I fixed it by changing the frontend from
to