MeWrite Docs

Flask: errorhandler(Exception)が404も捕まえてしまう

Flask 1.0以降でグローバル例外ハンドラが404 Not Foundも処理してしまう問題の解決策

概要

Flask 0.12から1.0にアップグレードすると、@app.errorhandler(Exception)で登録したグローバル例外ハンドラが404 Not Foundなどの HTTPException も捕まえるようになる問題。Flask 1.0での仕様変更が原因です。

エラーメッセージ

エラーメッセージは出ませんが、存在しないURLにアクセスすると404ではなく500が返ります。

1
2
3
4
5
6
7
# 期待する動作
$ curl http://localhost:5000/does-not-exist
# → 404 Not Found

# 実際の動作(Flask 1.0以降)
$ curl http://localhost:5000/does-not-exist
# → 500 Internal Server Error(カスタムハンドラの応答)

原因

  1. Flask 1.0の仕様変更: HTTPExceptionExceptionのサブクラスとして扱われるようになった
  2. グローバルハンドラの挙動: @errorhandler(Exception)が全ての例外を捕捉
  3. 404/405等も対象: NotFound、MethodNotAllowedなどもExceptionを継承

問題のあるコード

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from flask import Flask

app = Flask(__name__)

# ❌ NG: Flask 1.0以降、404も捕まえてしまう
@app.errorhandler(Exception)
def global_exception_handler(err):
    return "Internal Server Error", 500

@app.route("/")
def index():
    return "Hello World"

解決策

1. HTTPExceptionを除外する(推奨)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from flask import Flask
from werkzeug.exceptions import HTTPException

app = Flask(__name__)

@app.errorhandler(Exception)
def global_exception_handler(err):
    # HTTPExceptionは再送出して、Flaskのデフォルト処理に任せる
    if isinstance(err, HTTPException):
        return err

    # それ以外の例外を処理
    app.logger.error(f"Unhandled exception: {err}")
    return "Internal Server Error", 500

2. 特定の例外クラスのみ処理する

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from flask import Flask

app = Flask(__name__)

# ✅ OK: 特定の例外のみ処理
@app.errorhandler(ValueError)
def handle_value_error(err):
    return f"Invalid value: {err}", 400

@app.errorhandler(KeyError)
def handle_key_error(err):
    return f"Missing key: {err}", 400

# 404は別途ハンドラを定義(オプション)
@app.errorhandler(404)
def handle_not_found(err):
    return "Page not found", 404

3. HTTPExceptionのサブクラスを個別に処理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from flask import Flask
from werkzeug.exceptions import NotFound, MethodNotAllowed, InternalServerError

app = Flask(__name__)

@app.errorhandler(NotFound)
def handle_404(err):
    return {"error": "Not found"}, 404

@app.errorhandler(MethodNotAllowed)
def handle_405(err):
    return {"error": "Method not allowed"}, 405

@app.errorhandler(InternalServerError)
def handle_500(err):
    return {"error": "Internal server error"}, 500

# アプリケーション固有の例外
@app.errorhandler(Exception)
def handle_exception(err):
    from werkzeug.exceptions import HTTPException
    if isinstance(err, HTTPException):
        return err
    return {"error": "Unexpected error"}, 500

4. Flask 1.1以降: trap_http_exceptions設定

1
2
3
4
5
6
from flask import Flask

app = Flask(__name__)

# HTTPExceptionをトラップしない(Flask 1.1以降)
app.config['TRAP_HTTP_EXCEPTIONS'] = False

Flask バージョン別の挙動

バージョン@errorhandler(Exception)の挙動
0.12以前HTTPExceptionは捕捉しない
1.0〜1.0.xHTTPExceptionも捕捉する
1.1以降HTTPExceptionも捕捉する(設定で変更可)

APIでJSON応答を返す場合

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from flask import Flask, jsonify
from werkzeug.exceptions import HTTPException

app = Flask(__name__)

@app.errorhandler(HTTPException)
def handle_http_exception(err):
    """HTTPExceptionはそのままJSONで返す"""
    return jsonify({
        "error": err.name,
        "message": err.description
    }), err.code

@app.errorhandler(Exception)
def handle_exception(err):
    """その他の例外は500で返す"""
    if isinstance(err, HTTPException):
        return err

    app.logger.exception("Unhandled exception")
    return jsonify({
        "error": "Internal Server Error",
        "message": "An unexpected error occurred"
    }), 500

よくある間違い

  • HTTPExceptionをインポートせずにisinstance判定する
  • Flask 0.12のコードをそのまま1.0に移植する
  • 404のテストを書かずにアップグレードする

参考リンク

関連エラー

関連エラー

Python の他のエラー

最終更新: 2025-12-11