diff --git a/backend/app/api/endpoints/integrations.py b/backend/app/api/endpoints/integrations.py index 34152019..48fa9f57 100644 --- a/backend/app/api/endpoints/integrations.py +++ b/backend/app/api/endpoints/integrations.py @@ -362,8 +362,9 @@ async def list_boards( provider = await get_provider_for_account(current_user.account_id, db) boards = await provider.list_boards() return [PSABoardResponse(id=b.id, name=b.name) for b in boards] - except PSAError as e: - raise HTTPException(status_code=502, detail=str(e)) + except PSAError: + # Boards are optional UI chrome — degrade gracefully rather than surfacing a toast + return [] @router.get("/tickets/search", response_model=list[PSATicketSearchResult]) diff --git a/backend/app/services/flowpilot_engine.py b/backend/app/services/flowpilot_engine.py index 20c06299..1b280c29 100644 --- a/backend/app/services/flowpilot_engine.py +++ b/backend/app/services/flowpilot_engine.py @@ -330,6 +330,7 @@ async def start_session( # 7. Create first step step = _create_step_from_parsed( session_id=session.id, + account_id=session.account_id, step_order=0, parsed=parsed, input_tokens=input_tokens, @@ -433,6 +434,7 @@ async def process_response( # Create new step step = _create_step_from_parsed( session_id=session.id, + account_id=session.account_id, step_order=session.step_count - 1, parsed=parsed, input_tokens=input_tokens, @@ -694,6 +696,7 @@ async def pickup_session( briefing_step = AISessionStep( id=uuid.uuid4(), session_id=session.id, + account_id=session.account_id, branch_id=session.active_branch_id if session.is_branching else None, step_order=session.step_count, step_type="action", @@ -765,6 +768,7 @@ async def pickup_session( next_step = _create_step_from_parsed( session_id=session.id, + account_id=session.account_id, step_order=session.step_count - 1, parsed=parsed, input_tokens=input_tokens, @@ -997,6 +1001,7 @@ async def generate_status_update( step = AISessionStep( id=uuid.uuid4(), session_id=session.id, + account_id=session.account_id, branch_id=session.active_branch_id if session.is_branching else None, step_order=session.step_count, step_type="status_update", @@ -1440,6 +1445,7 @@ def _format_engineer_response(request: StepResponseRequest) -> str: def _create_step_from_parsed( session_id: UUID, + account_id: UUID, step_order: int, parsed: dict[str, Any], input_tokens: int, @@ -1487,6 +1493,7 @@ def _create_step_from_parsed( return AISessionStep( id=uuid.uuid4(), session_id=session_id, + account_id=account_id, branch_id=branch_id, step_order=step_order, step_type=step_type if parsed["type"] != "resolution_suggestion" else "action", diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 71e80ba5..89920130 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -49,9 +49,9 @@ function handleGlobalError(error: AxiosError) { return } - // Server errors (5xx) + // Server errors (5xx) — show backend detail when available, else generic message if (status >= 500) { - toast.error('Server error - please try again later') + toast.error(detail || 'Server error - please try again later') return } } diff --git a/frontend/src/hooks/useFlowPilotSession.ts b/frontend/src/hooks/useFlowPilotSession.ts index 873b3846..85b9d542 100644 --- a/frontend/src/hooks/useFlowPilotSession.ts +++ b/frontend/src/hooks/useFlowPilotSession.ts @@ -99,9 +99,14 @@ export function useFlowPilotSession(): UseFlowPilotSession { setAllSteps([firstStep]) setCurrentStep(firstStep) } catch (e: unknown) { - const message = e instanceof Error ? e.message : 'Failed to start session' + // Prefer the backend's detail message over the generic axios status string + const detail = (e as any)?.response?.data?.detail + const message = typeof detail === 'string' ? detail : (e instanceof Error ? e.message : 'Failed to start session') setError(message) - toast.error(message) + // Global axios interceptor already shows a toast for 5xx — skip duplicate + if (!(e as any)?.response?.status || (e as any)?.response?.status < 500) { + toast.error(message) + } } finally { setIsLoading(false) }