From 79686308407d48a9c2744348cc580fbbc0dafc86 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Mon, 27 Jul 2026 01:17:59 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20UploadFile=20ForwardRef=20+=20WebSocket?= =?UTF-8?q?=20403=20=E2=80=94=20root=20cause=20fixed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. plugin_error_handler.py: Remove _UploadFile alias, import UploadFile directly so FastAPI can resolve ForwardRef('UploadFile') in the wrapper's namespace. Also import WebSocket for ForwardRef resolution. 2. main.py: Skip WebSocket routes in wrap_plugin_route — WebSocket endpoints must not be wrapped (different protocol, no JSONResponse on error) --- app/core/plugin_error_handler.py | 3 ++- app/main.py | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/core/plugin_error_handler.py b/app/core/plugin_error_handler.py index f1bb496..1d41aa7 100644 --- a/app/core/plugin_error_handler.py +++ b/app/core/plugin_error_handler.py @@ -2,7 +2,8 @@ import logging import functools import inspect -from fastapi import UploadFile as _UploadFile # noqa: F401 — needed for ForwardRef resolution +from fastapi import UploadFile # noqa: F401 — needed for ForwardRef resolution +from fastapi import WebSocket # noqa: F401 — needed for ForwardRef resolution of WebSocket params from fastapi.responses import JSONResponse logger = logging.getLogger(__name__) diff --git a/app/main.py b/app/main.py index 2fcebd8..c42835e 100644 --- a/app/main.py +++ b/app/main.py @@ -11,6 +11,7 @@ from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, JSONResponse from fastapi.staticfiles import StaticFiles from starlette.middleware.base import BaseHTTPMiddleware +from starlette.routing import WebSocketRoute import importlib import logging import os @@ -434,8 +435,12 @@ def create_app() -> FastAPI: try: router_module = importlib.import_module(route_def.module) router = getattr(router_module, route_def.router_attr) - # Wrap each route handler with plugin error isolation + # Wrap each HTTP route handler with plugin error isolation + # Skip WebSocket routes — the wrapper breaks WS parameter + # resolution and returns JSONResponse instead of WS close. for route in router.routes: + if isinstance(route, WebSocketRoute): + continue if hasattr(route, 'endpoint'): route.endpoint = wrap_plugin_route(route.endpoint) # Add active-plugin check as a router-level dependency