UvicornとFastAPIとWebSocketsのトラブルについてのPerplexityの回答

以下MarkdownExportのコピペママ


FastAPI でWebsocketを使っていて次のようなエラーが出た。対処は?

AttributeError: ‘WebSocketProtocol’ object has no attribute ‘transfer_data_task’. Did you mean: ‘transfer_data_exc’?

このエラーは、websockets ライブラリ側のバグ(特定条件で transfer_data_task がセットされる前に close() が呼ばれる)で、FastAPI/Starlette/uvicorn を使った WebSocket でも報告されています。^1_1

原因の概要

  • uvicorn が内部で使っている websockets の「legacy implementation」で、 クライアントが接続直後に即切断するなどのタイミング次第で、
    • self.transfer_data_task がまだ作られていないのに
    • close() 内で await self.transfer_data_task が実行されてしまう
      という状況が起きます。^1_1
  • その結果として AttributeError: 'WebSocketProtocol' object has no attribute 'transfer_data_task' が投げられます。^1_1

対処方針(実務的)

いまのところアプリ側コードで「綺麗に握りつぶす」のは難しく、
ランタイムと依存パッケージの組み合わせを更新 or 変更するのが現実解です。

優先度順に書きます。

1. uvicorn / websockets を最新安定版にする

pip list などでバージョンを確認し、次のように更新してみてください。

pip install -U uvicorn websockets

websockets の issue で同じエラーが報告されており、
今後のバージョンで修正が入る見込みがあるため、まずは最新版で再現するか確認するのが早いです。^1_2

2. uvicorn の WebSocket 実装を切り替える(可能なら)

uvicorn には websockets 以外のバックエンド実装もあり、
オプションやバージョンによっては「Sans-I/O 実装」に切り替えを検討できます。^1_2

例(あくまで概念的・環境により変わる):

uvicorn app.main:app --ws websockets   # デフォルト
# → 将来的に別実装が選べる場合は --ws {impl} などで変更

--ws オプションで選べる実装や推奨設定は、使っている uvicorn のドキュメント・リリースノートを確認してください。^1_2

3. 一時的な割り切りとして

このエラーは「クライアントが過激に接続/切断を繰り返した時」(負荷テスト、激しいリロードなど)に出やすいと報告されています。^1_1

  • 実運用で頻発しないなら、ログレベルを調整して「既知のライブラリバグ」として扱う。
  • 「500 が返る」「プロセスが落ちる」など実害が大きい場合は、
    上記 1・2 のアップデート/切替を優先的に検討する。

もし可能なら教えてほしい情報

もう少し踏み込んだ対処(バージョン固定の提案など)もできますので、以下が分かると精度が上がります。

  • python / fastapi / starlette / uvicorn / websockets のバージョン
  • uvicorn の起動コマンド(--ws など)
  • エラーが出る条件(ブラウザのリロード連打など)

これらを教えてもらえれば、あなたの環境に即した具体的な組み合わせ案を出します。
^1_10^1_4^1_6^1_8

?


# python –version

Python 3.14.3

pip freeze

annotated-doc==0.0.4
annotated-types==0.7.0
anyio==4.12.1
arq==0.27.0
click==8.3.1
fastapi==0.128.1
gunicorn==25.0.1
h11==0.16.0
hiredis==3.3.0
idna==3.11
packaging==26.0
pydantic==2.12.5
pydantic_core==2.41.5
PyJWT==2.11.0
redis==5.3.1
starlette==0.50.0
typing-inspection==0.4.2
typing_extensions==4.15.0
uvicorn==0.40.0
watchfiles==1.1.1
websockets==16.0

この環境の uvicorn 0.40.0 + websockets 16.0 は最新で、このエラーは websockets legacy 実装の既知バグです。^2_1

推奨対処

uvicorn の --ws websockets-sansio オプションを使い、Sans-IO 実装に切り替えてください。
これで transfer_data_task 関連の内部 API 依存がなくなり、エラーが回避されます。^2_2

起動コマンド例

# 従来(デフォルト: legacy 実装でエラー発生)
uvicorn app.main:app --host 0.0.0.0 --port 8000

# 修正版(Sans-IO 実装に指定)
uvicorn app.main:app --host 0.0.0.0 --port 8000 --ws websockets-sansio
  • uvicorn 0.35.0 以降(あなたの 0.40.0 でOK)で Sans-IO が追加されました。^2_1
  • websockets 16.0 でも動作します。legacy API の非推奨問題を回避。^2_1

確認手順

  1. 上記コマンドで起動。
  2. エラーが再現する条件(例: クライアントの高速接続/切断)でテスト。
  3. ログに WebSocketsSansIOProtocol 系のメッセージが出れば成功。

なぜこれで解決するか

  • legacy 実装(デフォルト): websockets の古い private API を使い、transfer_data_task が未セット時の close() でクラッシュ。^2_1
  • Sans-IO 実装: Sans-I/O API(websockets 10.0+ の公式)を使い、より安定。uvicorn 開発者もこれを推奨。^2_3

追加アドバイス

  • Gunicorn 併用時: gunicorn 22.0.1 以降で UvicornWorker--ws websockets-sansio を渡せます。
gunicorn app.main:app -k uvicorn.workers.UvicornWorker --worker-args "--ws websockets-sansio"
  • 他オプション:
オプション説明
--ws wsprotowsproto ライブラリ使用(別インストール必要、pip install wsproto
--ws websocketslegacy(デフォルト・問題あり)
--ws websockets-sansio推奨・最新安定

これで直らない場合、再現コードや起動コマンド全文を教えてください。^2_1
^2_10^2_5^2_7^2_9

?

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です