Problem: I'm building a Flask backend using flask-restful, flask-jwt-extended, and PostgreSQL. When testing JWT token expiration via Postman, expired tokens consistently result in a 500 Internal Server Error instead of a clean 401 Unauthorized response. Desired Behavior: When a JWT token expires, my API should gracefully return a JSON response: {"message": "Token has expired"} Currently, an expired token results in this error: {"message": "Internal Server Error"} Server Logs Traceback: jwt.exceptions.ExpiredSignatureError: Signature has expired Relevent Code: Flask App Initialization (init.py) from flask import Flask, jsonify from flask_jwt_extended import JWTManager from flask_restful import Api from flask_cors import CORS from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate import os from dotenv import load_dotenv load_dotenv() app = Flask(__name__) CORS(app, supports_credentials=True) app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URL') app.config['JWT_SECRET_KEY'] = os.getenv('JWT_SECRET_KEY', 'temporary_secret') jwt = JWTManager(app) db = SQLAlchemy(app) api = Api(app) migrate = Migrate(app, db) # JWT error handlers @jwt.expired_token_loader def expired_token_callback(jwt_header, jwt_payload): return jsonify({"message": "Token has expired"}), 401 @jwt.invalid_token_loader def invalid_token_callback(error): return jsonify({"message": "Invalid token"}), 401 @jwt.unauthorized_loader def unauthorized_callback(error): return jsonify({"message": "Missing or invalid Authorization header"}), 401 if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True) Auth Resource (auth_resource.py) from flask_restful import Resource, reqparse from flask_jwt_extended import ( create_access_token, create_refresh_token, jwt_required, get_jwt_identity ) from werkzeug.security import check_password_hash from datetime import timedelta from app.models import User parser = reqparse.RequestParser() parser.add_argument('username', required=True) parser.add_argument('password', required=True) class LoginResource(Resource): def post(self): data = parser.parse_args() user = User.query.filter_by(username=data['username']).first() if user and check_password_hash(user.password_hash, data['password']): access_token = create_access_token(identity=user.id, expires_delta=timedelta(seconds=30)) refresh_token = create_refresh_token(identity=user.id, expires_delta=timedelta(minutes=2)) return {'access_token': access_token, 'refresh_token': refresh_token}, 200 return {'msg': 'Invalid credentials'}, 401 class ProtectedResource(Resource): @jwt_required() def get(self): identity = get_jwt_identity() return {'logged_in_as': identity}, 200 Testing Approach & Results (Postman) Login works (200 OK), returns tokens. Access protected resource (200 OK) initially with valid token. Wait 30 seconds (token expiration), then call protected resource again. Expected: 401 {"message": "Token has expired"} Actual: 500 Internal Server Error Server Logs: ERROR in app: Exception on /api/protected [GET] Traceback (most recent call last): File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 917, in full_dispatch_request rv = self.dispatch_request() File "/usr/local/lib/python3.11/site-packages/flask/app.py", line 902, in dispatch_request return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args) File "/usr/local/lib/python3.11/site-packages/flask_restful/__init__.py", line 604, in dispatch_request resp = meth(*args, **kwargs) File "/usr/local/lib/python3.11/site-packages/flask_jwt_extended/view_decorators.py", line 167, in decorator verify_jwt_in_request( File "/usr/local/lib/python3.11/site-packages/flask_jwt_extended/utils.py", line 128, in decode_token return jwt_manager._decode_jwt_from_config(encoded_token, csrf_value, allow_expired) File "/usr/local/lib/python3.11/site-packages/jwt/api_jwt.py", line 363, in _validate_exp raise ExpiredSignatureError("Signature has expired") jwt.exceptions.ExpiredSignatureError: Signature has expired What I've Tried without success: Implemented JWT global error handlers (expired_token_loader). Verified correct registration of JWT callbacks. Simplified the endpoint code to remove internal exception handling (as JWT errors should be handled globally). Fully rebuilt Docker containers multiple times to ensure fresh deployment. Questions Why isn't my global expired_token_loader capturing the expired token exceptions from the decorator? Is there an error in the way I've configured flask_jwt_extended with flask_restful that prevents global handlers from triggering? What steps can I take to isolate or debug the issue further? Environment Details Python: 3.11 (Dockerized) Flask: latest Flask-JWT-Extended: latest Flask-Restful: latest PostgreSQL database (Dockerized) Docker Compose setup for backend, frontend, and database Continue reading...