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
  • Community SeriesIdentify, Trace, and Fix Endpoint Regression Issues
  • ResourcesBackend Error Monitoring 101
  • Syntax.fm logo
    Listen to the Syntax Podcast

    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 100,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.

© 2024 • Sentry is a registered Trademark of Functional Software, Inc.