diff --git a/packages/keychain/src/components/ErrorAlert.tsx b/packages/keychain/src/components/ErrorAlert.tsx index 4981ab27da..596bd745f3 100644 --- a/packages/keychain/src/components/ErrorAlert.tsx +++ b/packages/keychain/src/components/ErrorAlert.tsx @@ -209,7 +209,9 @@ export function ControllerErrorAlert({ : error.message; } else { title = "Unknown error"; - description = error.message; + description = error.message || serializeErrorDetails(error); + copyText = serializeErrorDetails(error); + isExpanded = true; } return ( @@ -359,7 +361,9 @@ export function ControllerErrorAlert({ break; case ErrorCode.StarknetUnexpectedError: title = "Unexpected Error"; - description = error.data?.reason || JSON.stringify(error); + description = error.data?.reason || serializeErrorDetails(error); + isExpanded = true; + copyText = serializeErrorDetails(error); break; case ErrorCode.StarknetTransactionExecutionError: try { @@ -395,6 +399,9 @@ export function ControllerErrorAlert({ } default: { title = "Unknown Error"; + description = serializeErrorDetails(error); + isExpanded = true; + copyText = serializeErrorDetails(error); break; } } @@ -494,3 +501,69 @@ export function humanizeString(str: string): string { .replace(/^\w/, (c) => c.toUpperCase()) ); } + +/** + * Serializes error details including all enumerable properties + * This is especially useful for WASM/Rust errors that may have additional fields + * Handles circular references and non-serializable values safely + */ +export function serializeErrorDetails(error: unknown): string { + const errorObj: Record = {}; + + // Type guard to ensure error is an object + if (typeof error !== "object" || error === null) { + return String(error); + } + + // Get all enumerable properties from the error + for (const key in error) { + try { + const value = (error as Record)[key]; + // Skip functions and undefined values + if (typeof value !== "function" && value !== undefined) { + errorObj[key] = value; + } + } catch () { + // Skip properties that throw errors when accessed + continue; + } + } + + // Also try to get standard Error properties that might not be enumerable + const err = error as Partial; + if (err.message && !errorObj.message) { + errorObj.message = err.message; + } + if (err.name && !errorObj.name) { + errorObj.name = err.name; + } + if (err.stack && !errorObj.stack) { + errorObj.stack = err.stack; + } + + // Format as a readable string with circular reference handling + try { + const seen = new WeakSet(); + return JSON.stringify( + errorObj, + (key, value) => { + // Handle circular references + if (typeof value === "object" && value !== null) { + if (seen.has(value)) { + return "[Circular Reference]"; + } + seen.add(value); + } + // Handle BigInt + if (typeof value === "bigint") { + return value.toString(); + } + return value; + }, + 2, + ); + } catch (e) { + // Fallback if JSON.stringify still fails + return `Error serialization failed: ${String(e)}. Original error: ${String(error)}`; + } +}