From 4c34a1e056b34064f68e25df18396c1795d0a08a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Wed, 31 Jul 2024 15:23:19 -0300 Subject: [PATCH] fix: handle PydanticSerializationError that generates generic errors in the UI (#3108) * fix(main.py): handle PydanticSerializationError in JavaScriptMIMETypeMiddleware to provide detailed error message in case of serialization error * fix(main.py): handle PydanticSerializationError in JavaScriptMIMETypeMiddleware to provide detailed error message in case of serialization error * improve error handling * feat: Handle PydanticSerializationError in JavaScriptMIMETypeMiddleware Refactor the `JavaScriptMIMETypeMiddleware` class in `main.py` to handle the `PydanticSerializationError` exception. This change ensures that a detailed error message is provided in case of a serialization error. The error message is now serialized as a JSON string and included in the `detail` field of the `HTTPException` raised. * feat: Add tryParseJson function to utils.ts This commit adds a new function called `tryParseJson` to the `utils.ts` file. The function attempts to parse a JSON string and returns the parsed object. If parsing fails, it returns undefined. This function can be used to safely parse JSON strings without throwing an error. * fix: Handle error message in buildVertex function This commit modifies the buildVertex function in buildUtils.ts to handle error messages more effectively. It adds logic to extract the error message from the AxiosError response and parse it as JSON if possible. If the error message is not an array, it converts it into an array. This change ensures that the error message is properly displayed when building a component fails. * remove console.log * remove not related code --------- Co-authored-by: anovazzi1 (cherry picked from commit f08467d84ba6558a5450fced31a2de8104a8bee8) --- src/backend/base/langflow/main.py | 17 +++++++++++------ src/frontend/src/utils/buildUtils.ts | 14 ++++++++++---- src/frontend/src/utils/utils.ts | 16 ++++++++++++++++ 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/backend/base/langflow/main.py b/src/backend/base/langflow/main.py index 697f74c95ab..ca723ea299d 100644 --- a/src/backend/base/langflow/main.py +++ b/src/backend/base/langflow/main.py @@ -1,24 +1,26 @@ -import os import asyncio +import json +import os import warnings from contextlib import asynccontextmanager +from http import HTTPStatus from pathlib import Path from typing import Optional from urllib.parse import urlencode import nest_asyncio # type: ignore -from fastapi import FastAPI, Request, Response, HTTPException +from fastapi import FastAPI, HTTPException, Request, Response from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, JSONResponse from fastapi.staticfiles import StaticFiles -from http import HTTPStatus from loguru import logger +from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor from pydantic import PydanticDeprecatedSince20 +from pydantic_core import PydanticSerializationError from rich import print as rprint from starlette.middleware.base import BaseHTTPMiddleware -from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor -from langflow.api import router, health_check_router, log_router +from langflow.api import health_check_router, log_router, router from langflow.initial_setup.setup import ( create_or_update_starter_projects, initialize_super_user_if_needed, @@ -67,7 +69,10 @@ async def dispatch(self, request: Request, call_next): try: response = await call_next(request) except Exception as exc: - logger.error(exc) + if isinstance(exc, PydanticSerializationError): + message = "Something went wrong while serializing the response. Please share this error on our GitHub repository." + error_messages = json.dumps([message, str(exc)]) + raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=error_messages) from exc raise exc if "files/" not in request.url.path and request.url.path.endswith(".js") and response.status_code == 200: response.headers["Content-Type"] = "text/javascript" diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index 35e657180b7..ad9ac48688f 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -7,6 +7,7 @@ import useFlowStore from "../stores/flowStore"; import { VertexBuildTypeAPI } from "../types/api"; import { isErrorLogType } from "../types/utils/typeCheckingUtils"; import { VertexLayerElementType } from "../types/zustand/flow"; +import { tryParseJson } from "./utils"; type BuildVerticesParams = { setLockChat?: (lock: boolean) => void; @@ -306,12 +307,17 @@ async function buildVertex({ buildResults.push(buildData.valid); } catch (error) { console.error(error); + let errorMessage: string | string[] = + (error as AxiosError).response?.data?.detail || + (error as AxiosError).response?.data?.message || + "An unexpected error occurred while building the Component. Please try again."; + errorMessage = tryParseJson(errorMessage as string) ?? errorMessage; + if (!Array.isArray(errorMessage)) { + errorMessage = [errorMessage]; + } onBuildError!( "Error Building Component", - [ - (error as AxiosError).response?.data?.detail ?? - "An unexpected error occurred while building the Component. Please try again.", - ], + errorMessage, verticesIds.map((id) => ({ id })), ); buildResults.push(false); diff --git a/src/frontend/src/utils/utils.ts b/src/frontend/src/utils/utils.ts index 313fe77be8b..8b3bd0336e4 100644 --- a/src/frontend/src/utils/utils.ts +++ b/src/frontend/src/utils/utils.ts @@ -546,3 +546,19 @@ export function generateBackendColumnsFromValue(rows: Object[]): ColumnField[] { return newColumn; }); } + +/** + * Tries to parse a JSON string and returns the parsed object. + * If parsing fails, returns undefined. + * + * @param json - The JSON string to parse. + * @returns The parsed JSON object, or undefined if parsing fails. + */ +export function tryParseJson(json: string) { + try { + const parsedJson = JSON.parse(json); + return parsedJson; + } catch (error) { + return; + } +}