Sentry Answers>FastAPI>

Solve FastAPI "field required" and "value_error.missing" errors

Solve FastAPI "field required" and "value_error.missing" errors

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", "firstname" ], "msg": "field required", "type": "value_error.missing" }, { "loc": [ "body", "lastname" ], "msg": "field required", "type": "value_error.missing" }, { "loc": [ "body", "age" ], "msg": "field required", "type": "value_error.missing" } ] }

My client script looks like this:

Click to Copy
import requests response = requests.post('http://localhost:8000/create-user/', data={ "firstname": "Robert", "surname": "De Niro", "age": 80 }) print("Response Status Code:", response.status_code) 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. An argument passed to the data parameter of requests.post will be submitted as form data, but the API expects JSON – this is why the error message seems to indicate that no data was received.

We can solve this by using the json parameter instead of the data parameter in our function call:

Click to Copy
import requests response = requests.post('http://localhost:8000/create-user/', json={ "firstname": "Robert", "surname": "De Niro", "age": 80 }) print(response.json())

This small change will cause requests to send our dictionary as JSON data, which should make our client and API endpoint work together as expected.

Alternatively, we could modify the FastAPI endpoint to accept form data, but this requires a few more code modifications. Following the FastAPI documentation, we could install python-multipart:

Click to Copy
pip install python-multipart

Then, we can modify the FastAPI endpoint to accept form data:

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

Note that instead of relying on the fields defined in our Pydantic model, we need to define each field we expect this endpoint to accept. This may be useful if we’re building an endpoint that should only receive some of the fields in a particular model. It is also useful if we would like our endpoint to be used by a simple HTML form rather than a client submitting JSON data.

For more about using Annotated, please see this answer.

  • 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.