サイトアイコン IT & ライフハックブログ|学びと実践のためのアイデア集

FastAPIのよくあるエラー10選:原因の切り分け方と解決手順

green snake

Photo by Pixabay on Pexels.com

FastAPIのエラーは、表示された文言だけを検索するより、どの層で失敗したかを切り分けると解決しやすくなります。まず、実行中のPython環境、インポート元、入力データ、戻り値、Uvicornの起動設定を順に確認します。

FastAPIの特徴とAPI設計の基本を先に把握しておくと、フレームワーク、データ検証、サーバーのどこで問題が起きたかを整理しやすくなります。この記事では、既存の10項目を保ちながら、原因を決めつけない確認手順に改めました。

エラーを直す前に確認すること

パスオペレーションは、URLのパスとGETやPOSTなどのHTTPメソッドを組み合わせたAPIの処理単位です。FastAPIでは、@app.get('/items')のようなデコレーターと、その直下の関数で定義します。

  1. トレースバックの最終行だけでなく、最初に自分のファイルが現れる行まで読む。
  2. python -m pip show fastapi pydantic uvicornで、実行中のPython環境に入っているパッケージを確認する。
  3. エラーが起きる処理を小さくし、インポート、入力、戻り値、起動設定のどこで失敗するかを分ける。
  4. 修正後は同じリクエストを再実行し、別のエラーに変わっていないかも確認する。
10種類のエラーと最初の確認箇所
エラー 最初に確認する箇所
ModuleNotFoundError Python環境とインストール先
ImportError インポート元のファイル
pydantic.BaseModelAttributeError インポート方法とPydanticの実体
ResponseTypeError レスポンスを扱う行
Field required 不足フィールドと入力位置
レスポンス型に関するAssertionError デコレーター、戻り値、型注釈
Event loop is already running イベントループの起動元
JSONDecodeError 送信内容または受信内容
Invalid hostname Uvicornのhost
404 Not Found HTTPメソッドとルート定義

1. ModuleNotFoundError: No module named 'fastapi'

原因

コマンドを実行しているPython環境からFastAPIを見つけられない状態です。FastAPIが未インストールの場合だけでなく、別の仮想環境へインストールしている場合にも発生します。

確認と対応

まず、現在の環境にFastAPIがあるかを確認します。

python -m pip show fastapi

見つからなければ、同じPythonを通してFastAPIとUvicornをインストールします。

python -m pip install fastapi uvicorn

仮想環境を作成済みなら、その環境を有効にしてから再確認します。

# Windows
venv\Scripts\activate

# macOS / Linux
source venv/bin/activate

2. ImportError: cannot import name 'FastAPI' from 'fastapi'

原因

from fastapi import FastAPIが別のファイルを読み込んでいるか、インストール状態に問題がある可能性があります。プロジェクト内のfastapi.pyが公式パッケージより先に読み込まれる、名前の衝突は代表的な原因です。

確認と対応

実際に読み込まれている場所を確認します。

python -c "import fastapi; print(fastapi.__file__)"

出力がプロジェクト内のfastapi.pyを指していたら、そのファイルを別名に変更します。公式パッケージを指しているのに直らない場合は、同じ環境で再インストールします。

python -m pip uninstall fastapi
python -m pip install fastapi

3. AttributeError: module 'pydantic' has no attribute 'BaseModel'

原因

BaseModelpydanticからインポートします。このエラーをPydanticのメジャーバージョンだけで判断すると、別の原因を見落とします。ローカルのpydantic.pyを読み込んでいる場合や、環境内のパッケージ構成が崩れている場合も切り分けが必要です。

確認と対応

インポートは次の形にします。

from pydantic import BaseModel

続いて、バージョンと読み込み元を確認します。

python -c "import pydantic; print(pydantic.__version__); print(pydantic.__file__)"

プロジェクト内の同名ファイルが表示されたら名前を変更します。バージョンを下げる前に、使用中のFastAPIとPydanticの組み合わせ、移行中のコード、依存関係を確認してください。FastAPIの型定義と入力検証の設計も、モデルの役割を整理する際に参考になります。

4. TypeError: object of type 'Response' has no len()

原因

この文言は、Responseを文字列や配列のように扱い、長さを求めた場所で発生します。カスタム処理や周辺ライブラリを経由することもあるため、「contentがない」と最初から決めつけず、トレースバックでlen()を呼んだ行を確認します。

確認と対応

テキストを直接返す目的なら、内容とメディアタイプを明示したResponseを返せます。

from fastapi import FastAPI, Response

app = FastAPI()

@app.get('/')
def read_root():
    return Response(content='Hello, World!', media_type='text/plain')

JSONを返すだけなら、Responseを自分で組み立てず、辞書を返すほうが処理の意図は明確です。

@app.get('/status')
def read_status():
    return {'status': 'ok'}

5. Field requiredまたはtype=value_error.missing

原因

Pydanticモデルで必須と定義したフィールドが、入力データに含まれていません。キー名の間違い、入れ子の位置の違い、クライアントが古い形式を送っている場合も、同じ種類の入力検証エラーにつながります。エラー表記はPydanticの世代によって異なることがあります。

確認と対応

エラー詳細に示されたフィールド名と位置を確認し、クライアントから必須項目を送ります。次のnameは必須で、priceにはデフォルト値があります。

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float = 0.0
{
  "name": "Apple",
  "price": 10.5
}

ただし、エラーを消す目的だけでデフォルト値を追加すると、必須だった入力を任意に変えてしまいます。priceを省略してよい仕様なのかを確認してから変更します。

6. AssertionError: Path operations must have at least one response type

原因

この種のレスポンス型に関するAssertionErrorは、FastAPI本体、使用している拡張、バージョンによって発生条件が変わります。関数がNoneを返したという説明だけでは原因を特定できません。

確認と対応

エラーが起きたパスオペレーションについて、デコレーターのresponse_modelresponse_class、関数の戻り値、戻り値の型注釈が矛盾していないかを確認します。まずは、単純な辞書を返す最小構成で起動できるかを試します。

from fastapi import FastAPI

app = FastAPI()

@app.get('/')
def read_root():
    return {'message': 'Hello World'}

最小構成で動くなら、元のルートへ設定を一つずつ戻すと、問題のある指定を絞り込めます。

7. RuntimeError: Event loop is already running

原因

Jupyter Notebookなど、すでに非同期処理のイベントループが動いている環境で、別のイベントループを開始しようとすると発生します。イベントループは、非同期の処理を実行する順番と待機を管理する仕組みです。

確認と対応

Notebook内でさらにasyncio.run()を呼んでいないかを確認します。可能ならUvicornはNotebookの外部プロセスとして起動し、Notebookでは既存のループ上でawaitを使います。

既存のNotebook構成を変えられない場合、nest_asyncioを使う方法もあります。ただし、イベントループの動作を変更する回避策なので、適用範囲を限定します。

import nest_asyncio
import uvicorn

nest_asyncio.apply()
uvicorn.run(app, host='127.0.0.1', port=8000)

8. JSONDecodeError: Expecting value: line 1 column 1 (char 0)

原因

JSONとして解釈しようとしたデータが空か、JSONの構文になっていません。リクエスト送信時に起きる場合と、クライアントが空のレスポンスやJSON以外のレスポンスを解析したときに起きる場合を分けて確認します。

確認と対応

from fastapi import Request

@app.post('/debug')
async def debug_request(request: Request):
    body = await request.body()
    print(body)
    return {'received': True}

リクエスト本文には機密情報が含まれることがあるため、この確認用ログは開発中だけに限定します。

9. ValueError: Invalid hostname(Uvicorn起動時)

原因

Uvicornのhostに、バインドできない値を渡しています。hostにはURL全体ではなく、ホストまたはIPアドレスを指定します。

確認と対応

ローカル環境からだけ接続するなら127.0.0.1を使います。

uvicorn.run(app, host='127.0.0.1', port=8000)

すべてのネットワークインターフェースで待ち受ける場合は、コマンドラインで次のように指定します。

uvicorn main:app --host 0.0.0.0 --port 8000

0.0.0.0は待ち受け先の指定です。ブラウザーやAPIクライアントから接続するときは、実際のホスト名またはIPアドレスを使います。

10. StarletteHTTPException: 404 Not Found

原因

指定したHTTPメソッドとパスに一致するルートがないか、存在しない対象をアプリケーションが404として返しています。カスタム例外ハンドラーを使っている環境では、ログにStarletteHTTPExceptionという型名が出ることもあります。

確認と対応

リクエストのメソッド、URL、ルーターの接頭辞を、実際の定義と照合します。次の例に一致するのは、GETメソッドの/items/123です。

from fastapi import FastAPI

app = FastAPI()

@app.get('/items/{item_id}')
def read_item(item_id: int):
    return {'item_id': item_id}

ルートには到達しているものの対象データがない場合は、意図した404なのかを例外処理とログで確認します。ルート自体が見つからない404とは、修正する場所が異なります。

再発を減らす確認順序

FastAPIのエラーは、次の順序で確認すると原因を混同しにくくなります。

  1. 実行中のPythonと仮想環境をそろえる。
  2. パッケージの読み込み元とバージョンを確認する。
  3. Pydanticモデルと送信JSONのフィールドを照合する。
  4. パスオペレーションの戻り値とレスポンス設定を照合する。
  5. Uvicornの起動場所、イベントループ、ホスト設定を確認する。

一度に複数の設定を変えると、どの変更で直ったかが分かりません。最小構成で再現し、一項目ずつ戻す方法なら、同じ種類のエラーが再発したときにも判断材料を残せます。

モバイルバージョンを終了