Sentry Answers>FastAPI>

POST request to FastAPI endpoint produces error 422: "value is not a valid dict"

POST request to FastAPI endpoint produces error 422: "value is not a valid dict"

David Y.

The Problem

I have a Python script that sends a POST request to an API I’ve built using FastAPI. The POST request fails with error code 422 and the following details:

Click to Copy
{ "detail": [ { "loc": [ "body" ], "msg": "value is not a valid dict", "type": "type_error.dict" } ] }

My client script looks like this:

Click to Copy
import requests, pandas, json dataframe = pandas.DataFrame({ "Firstname": ["Joaquin"], "Surname": ["Phoenix"], "Age": [49] }) df_json = dataframe.to_json(orient="records") response = requests.post('http://localhost:8000/create-user/', json=json.loads(df_json), headers={"Content-Type": "application/json"}) print(response.json())

The relevant API endpoint and Pydantic model look like this:

Click to Copy
from pydantic import BaseModel from fastapi import FastAPI app = FastAPI() class User(BaseModel): firstname: str surname: str age: int @app.post("/create-user/") async def create_users(user: User): # ... user creation operations ... return {"Message": "User created."}

What’s going wrong? Is it a problem with my client script or my API code?

The Solution

The problem is in the client script. The requests.post function expects a dictionary to be passed to the json keyword argument, which it will automatically convert to JSON.

This script first converts a Pandas DataFrame to a JSON string (dataframe.to_json(orient="records")), which looks like this:

Click to Copy
[{"Firstname":"Joaquin", "Surname": "Phoenix","Age": 49}]

The script then uses json.loads to convert it back into a Python object. Because of the argument orient="records" in the original conversion, the object created here will be a list rather than a dictionary, leading to the 422 error when it is submitted.

We can fix this problem by converting the DataFrame to a dictionary instead of a JSON string:

Click to Copy
import requests, pandas dataframe = pandas.DataFrame({ "Firstname": ["Joaquin"], "Surname": ["Phoenix"], "Age": [49] }) userdata = dataframe.to_dict(orient="records") # will be a list: [{"Firstname":"Joaquin", "Surname": "Phoenix","Age": 49}] userdata = userdata[0] # take the first item from the list # will be a dictionary: {"Firstname":"Joaquin", "Surname": "Phoenix","Age": 49} prediction = requests.post('http://localhost:8000/create-users/', json=userdata, headers={"Content-Type": "application/json"})

This altered code should produce the expected results.

  • Syntax.fmListen to the Syntax Podcast (opens in a new tab)
  • Community SeriesIdentify, Trace, and Fix Endpoint Regression Issues (opens in a new tab)
  • ResourcesBackend Error Monitoring 101 (opens in a new tab)
  • Syntax.fm logo
    Listen to the Syntax Podcast (opens in a new tab)

    Tasty treats for web developers brought to you by Sentry. Get tips and tricks from Wes Bos and Scott Tolinski.

    SEE EPISODES

Considered “not bad” by 4 million developers and more than 150,000 organizations worldwide, Sentry provides code-level observability to many of the world’s best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.