From 77870b0fc737ace4ec8ae9480d5128165ed5abe6 Mon Sep 17 00:00:00 2001 From: Abhimanyu Saharan Date: Tue, 3 Mar 2026 03:09:29 +0530 Subject: [PATCH] fix(agent): improve error handling for get_agent_soul method --- backend/app/api/agent.py | 24 ++++++++++++++++++----- backend/app/core/auth.py | 19 +++++------------- backend/app/schemas/task_custom_fields.py | 13 +++++++++++- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/backend/app/api/agent.py b/backend/app/api/agent.py index a290420b..6d930b9e 100644 --- a/backend/app/api/agent.py +++ b/backend/app/api/agent.py @@ -1429,11 +1429,25 @@ async def get_agent_soul( target_agent_id=agent_id, ) coordination = GatewayCoordinationService(session) - return await coordination.get_agent_soul( - board=board, - target_agent_id=agent_id, - correlation_id=f"soul.read:{board.id}:{agent_id}", - ) + try: + return await coordination.get_agent_soul( + board=board, + target_agent_id=agent_id, + correlation_id=f"soul.read:{board.id}:{agent_id}", + ) + except HTTPException as exc: + # Keep explicit auth/not-found responses, but avoid relaying internal 5xx details. + if exc.status_code >= status.HTTP_500_INTERNAL_SERVER_ERROR: + raise HTTPException( + status_code=exc.status_code, + detail="Gateway SOUL read failed", + ) from exc + raise + except Exception as exc: # pragma: no cover - defensive API boundary guard + raise HTTPException( + status_code=status.HTTP_502_BAD_GATEWAY, + detail="Gateway SOUL read failed", + ) from exc @router.put( diff --git a/backend/app/core/auth.py b/backend/app/core/auth.py index 7007b876..21d4178d 100644 --- a/backend/app/core/auth.py +++ b/backend/app/core/auth.py @@ -237,7 +237,6 @@ async def _authenticate_clerk_request(request: Request) -> RequestState: async def _fetch_clerk_profile(clerk_user_id: str) -> tuple[str | None, str | None]: secret = settings.clerk_secret_key.strip() - secret_kind = secret.split("_", maxsplit=1)[0] if "_" in secret else "unknown" server_url = _normalize_clerk_server_url(settings.clerk_api_url or "") clerk_user_id_log = clerk_user_id[-6:] if clerk_user_id else "" @@ -252,28 +251,24 @@ async def _fetch_clerk_profile(clerk_user_id: str) -> tuple[str | None, str | No return email, name except ClerkErrors as exc: logger.warning( - "auth.clerk.profile.fetch_failed clerk_user_id=%s reason=clerk_errors " - "secret_kind=%s error_type=%s", + "auth.clerk.profile.fetch_failed clerk_user_id=%s reason=clerk_errors " "error_type=%s", clerk_user_id_log, - secret_kind, exc.__class__.__name__, ) except SDKError as exc: logger.warning( "auth.clerk.profile.fetch_failed clerk_user_id=%s status=%s reason=sdk_error " - "server_url=%s secret_kind=%s", + "server_url=%s", clerk_user_id_log, exc.status_code, server_url, - secret_kind, ) except httpx.TimeoutException as exc: logger.warning( "auth.clerk.profile.fetch_failed clerk_user_id=%s reason=timeout " - "server_url=%s secret_kind=%s error=%s", + "server_url=%s error=%s", clerk_user_id_log, server_url, - secret_kind, str(exc) or exc.__class__.__name__, ) except Exception as exc: @@ -293,7 +288,6 @@ async def delete_clerk_user(clerk_user_id: str) -> None: return secret = settings.clerk_secret_key.strip() - secret_kind = secret.split("_", maxsplit=1)[0] if "_" in secret else "unknown" server_url = _normalize_clerk_server_url(settings.clerk_api_url or "") clerk_user_id_log = clerk_user_id[-6:] if clerk_user_id else "" @@ -307,10 +301,8 @@ async def delete_clerk_user(clerk_user_id: str) -> None: logger.info("auth.clerk.user.delete clerk_user_id=%s", clerk_user_id_log) except ClerkErrors as exc: logger.warning( - "auth.clerk.user.delete_failed clerk_user_id=%s reason=clerk_errors " - "secret_kind=%s error_type=%s", + "auth.clerk.user.delete_failed clerk_user_id=%s reason=clerk_errors " "error_type=%s", clerk_user_id_log, - secret_kind, exc.__class__.__name__, ) raise HTTPException( @@ -323,11 +315,10 @@ async def delete_clerk_user(clerk_user_id: str) -> None: return logger.warning( "auth.clerk.user.delete_failed clerk_user_id=%s status=%s reason=sdk_error " - "server_url=%s secret_kind=%s", + "server_url=%s", clerk_user_id_log, exc.status_code, server_url, - secret_kind, ) raise HTTPException( status_code=status.HTTP_502_BAD_GATEWAY, diff --git a/backend/app/schemas/task_custom_fields.py b/backend/app/schemas/task_custom_fields.py index ba2e4232..947d7878 100644 --- a/backend/app/schemas/task_custom_fields.py +++ b/backend/app/schemas/task_custom_fields.py @@ -4,6 +4,7 @@ from __future__ import annotations import re from datetime import date, datetime +from functools import lru_cache from typing import Literal, Self from urllib.parse import urlparse from uuid import UUID @@ -297,6 +298,12 @@ def _parse_iso_datetime(value: str) -> datetime: return datetime.fromisoformat(normalized) +@lru_cache(maxsize=256) +def _compiled_validation_regex(pattern: str) -> re.Pattern[str]: + """Compile and cache validation regex patterns for value checks.""" + return re.compile(pattern) + + def validate_custom_field_value( *, field_type: TaskCustomFieldType, @@ -346,7 +353,11 @@ def validate_custom_field_value( if validation_regex is not None and field_type in STRING_FIELD_TYPES: if not isinstance(value, str): raise ValueError("must be a string for regex validation") - if re.fullmatch(validation_regex, value) is None: + try: + pattern = _compiled_validation_regex(validation_regex) + except re.error as exc: + raise ValueError(f"validation_regex is invalid: {exc}") from exc + if pattern.fullmatch(value) is None: raise ValueError("does not match validation_regex")