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:

{ "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:

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:

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:

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:

pip install python-multipart

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

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.

Loved by over 4 million developers and more than 90,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.

Share on Twitter
Bookmark this page
Ask a questionJoin the discussion

Related Answers

A better experience for your users. An easier life for your developers.

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