diff --git a/surfsense_web/lib/apis/base-api.service.ts b/surfsense_web/lib/apis/base-api.service.ts index 31341a9e6..872c885d9 100644 --- a/surfsense_web/lib/apis/base-api.service.ts +++ b/surfsense_web/lib/apis/base-api.service.ts @@ -10,6 +10,7 @@ import { AuthorizationError, NetworkError, NotFoundError, + type ValidationFieldError, } from "../error"; enum ResponseType { @@ -165,6 +166,9 @@ class BaseApiService { const requestId: string | undefined = envelope?.request_id ?? response.headers.get("X-Request-ID") ?? undefined; const reportUrl: string | undefined = envelope?.report_url; + const validationFields: ValidationFieldError[] | undefined = Array.isArray(envelope?.fields) + ? envelope.fields + : undefined; // Handle 401 - try to refresh token first (only once) if (response.status === 401) { @@ -209,8 +213,8 @@ class BaseApiService { response.status, response.statusText ); - default: - throw new AppError( + default: { + const appError = new AppError( errorMessage || "Something went wrong", response.status, response.statusText, @@ -218,6 +222,9 @@ class BaseApiService { requestId, reportUrl ); + appError.fields = validationFields; + throw appError; + } } } refreshRetryBlockedUntil.delete(getRefreshRetryKey(mergedOptions.method, url)); diff --git a/surfsense_web/lib/error.ts b/surfsense_web/lib/error.ts index 10cb3706e..8c87007b8 100644 --- a/surfsense_web/lib/error.ts +++ b/surfsense_web/lib/error.ts @@ -1,11 +1,19 @@ export const SURFSENSE_ISSUES_URL = "https://github.com/MODSetter/SurfSense/issues"; +/** One field-level failure from a 422 validation response. */ +export interface ValidationFieldError { + loc: string[]; + msg: string; +} + export class AppError extends Error { status?: number; statusText?: string; code?: string; requestId?: string; reportUrl?: string; + /** Per-field failures from a 422 response, keyed by their location path. */ + fields?: ValidationFieldError[]; constructor( message: string, status?: number,