2025-08-08 16:01:56 +01:00
|
|
|
/**
|
|
|
|
|
* Standardized error handling utilities for tool operations
|
|
|
|
|
*/
|
|
|
|
|
|
2026-04-10 17:41:19 +01:00
|
|
|
import { normalizeAxiosErrorData } from "@app/services/errorUtils";
|
2025-11-20 14:54:41 +00:00
|
|
|
|
2025-08-08 16:01:56 +01:00
|
|
|
/**
|
|
|
|
|
* Default error extractor that follows the standard pattern
|
|
|
|
|
*/
|
|
|
|
|
export const extractErrorMessage = (error: any): string => {
|
2026-04-10 17:41:19 +01:00
|
|
|
if (error.response?.data && typeof error.response.data === "string") {
|
2025-08-08 16:01:56 +01:00
|
|
|
return error.response.data;
|
|
|
|
|
}
|
|
|
|
|
if (error.message) {
|
|
|
|
|
return error.message;
|
|
|
|
|
}
|
2026-04-10 17:41:19 +01:00
|
|
|
return "There was an error processing your request.";
|
2025-08-08 16:01:56 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a standardized error handler for tool operations
|
|
|
|
|
* @param fallbackMessage - Message to show when no specific error can be extracted
|
|
|
|
|
* @returns Error handler function that follows the standard pattern
|
|
|
|
|
*/
|
|
|
|
|
export const createStandardErrorHandler = (fallbackMessage: string) => {
|
|
|
|
|
return (error: any): string => {
|
2026-04-10 17:41:19 +01:00
|
|
|
if (error.response?.data && typeof error.response.data === "string") {
|
2025-08-08 16:01:56 +01:00
|
|
|
return error.response.data;
|
|
|
|
|
}
|
|
|
|
|
if (error.message) {
|
|
|
|
|
return error.message;
|
|
|
|
|
}
|
|
|
|
|
return fallbackMessage;
|
|
|
|
|
};
|
2025-11-20 14:54:41 +00:00
|
|
|
};
|
2026-03-06 13:46:40 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parses a 422 response, extracts errored file IDs from the payload (JSON or UUID regex),
|
|
|
|
|
* and marks them in the UI. Returns true if IDs were found and handled, false otherwise.
|
|
|
|
|
*/
|
2026-04-10 17:41:19 +01:00
|
|
|
export const handle422Error = async (error: any, markFileError: (fileId: string) => void): Promise<boolean> => {
|
2026-03-06 13:46:40 +00:00
|
|
|
const status = error?.response?.status;
|
2026-04-10 17:41:19 +01:00
|
|
|
if (typeof status !== "number" || status !== 422) return false;
|
2026-03-06 13:46:40 +00:00
|
|
|
|
|
|
|
|
const payload = error?.response?.data;
|
|
|
|
|
let parsed: unknown = payload;
|
|
|
|
|
|
2026-04-10 17:41:19 +01:00
|
|
|
if (typeof payload === "string") {
|
|
|
|
|
try {
|
|
|
|
|
parsed = JSON.parse(payload);
|
|
|
|
|
} catch {
|
|
|
|
|
parsed = payload;
|
|
|
|
|
}
|
|
|
|
|
} else if (payload && typeof (payload as Blob).text === "function") {
|
2026-03-06 13:46:40 +00:00
|
|
|
const text = await (payload as Blob).text();
|
2026-04-10 17:41:19 +01:00
|
|
|
try {
|
|
|
|
|
parsed = JSON.parse(text);
|
|
|
|
|
} catch {
|
|
|
|
|
parsed = text;
|
|
|
|
|
}
|
2026-03-06 13:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let ids: string[] | undefined = Array.isArray((parsed as { errorFileIds?: unknown })?.errorFileIds)
|
|
|
|
|
? (parsed as { errorFileIds: string[] }).errorFileIds
|
|
|
|
|
: undefined;
|
|
|
|
|
|
2026-04-10 17:41:19 +01:00
|
|
|
if (!ids && typeof parsed === "string") {
|
2026-03-06 13:46:40 +00:00
|
|
|
const match = parsed.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g);
|
|
|
|
|
if (match && match.length > 0) ids = Array.from(new Set(match));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ids && ids.length > 0) {
|
|
|
|
|
for (const id of ids) {
|
2026-04-10 17:41:19 +01:00
|
|
|
try {
|
|
|
|
|
markFileError(id);
|
|
|
|
|
} catch (_e) {
|
|
|
|
|
void _e;
|
|
|
|
|
}
|
2026-03-06 13:46:40 +00:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
};
|
2025-11-20 14:54:41 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Handles password-related errors with status code checking
|
|
|
|
|
* @param error - The error object from axios
|
|
|
|
|
* @param incorrectPasswordMessage - Message to show for incorrect password (typically 500 status)
|
|
|
|
|
* @param fallbackMessage - Message to show for other errors
|
|
|
|
|
* @returns Error message string
|
|
|
|
|
*/
|
|
|
|
|
export const handlePasswordError = async (
|
|
|
|
|
error: any,
|
|
|
|
|
incorrectPasswordMessage: string,
|
2026-04-10 17:41:19 +01:00
|
|
|
fallbackMessage: string,
|
2025-11-20 14:54:41 +00:00
|
|
|
): Promise<string> => {
|
|
|
|
|
const status = error?.response?.status;
|
|
|
|
|
|
|
|
|
|
// Handle specific error cases with user-friendly messages
|
2026-04-13 13:20:43 +01:00
|
|
|
// Backend returns 400 with PdfPasswordException for incorrect/missing PDF passwords
|
2025-11-20 14:54:41 +00:00
|
|
|
if (status === 500) {
|
|
|
|
|
return incorrectPasswordMessage;
|
|
|
|
|
}
|
2026-04-13 13:20:43 +01:00
|
|
|
if (status === 400) {
|
|
|
|
|
const data = error?.response?.data;
|
|
|
|
|
// ProblemDetail JSON has type "/errors/pdf-password", blob needs parsing
|
|
|
|
|
const isPasswordError = await (async () => {
|
|
|
|
|
if (data instanceof Blob) {
|
|
|
|
|
try {
|
|
|
|
|
const text = await data.text();
|
|
|
|
|
return text.includes("pdf-password") || text.includes("passworded");
|
|
|
|
|
} catch {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const type = data?.type ?? "";
|
|
|
|
|
return type.includes("pdf-password");
|
|
|
|
|
})();
|
|
|
|
|
if (isPasswordError) {
|
|
|
|
|
return incorrectPasswordMessage;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-20 14:54:41 +00:00
|
|
|
|
|
|
|
|
// For other errors, try to extract the message
|
|
|
|
|
const normalizedData = await normalizeAxiosErrorData(error?.response?.data);
|
|
|
|
|
const errorWithNormalizedData = {
|
|
|
|
|
...error,
|
|
|
|
|
response: {
|
|
|
|
|
...error?.response,
|
2026-04-10 17:41:19 +01:00
|
|
|
data: normalizedData,
|
|
|
|
|
},
|
2025-11-20 14:54:41 +00:00
|
|
|
};
|
|
|
|
|
return extractErrorMessage(errorWithNormalizedData) || fallbackMessage;
|
2026-04-10 17:41:19 +01:00
|
|
|
};
|