@@ -798,38 +798,75 @@ export async function parseOptionsAndRunCLI(argsToParse: string[]) {
798798 * plain objects (which lose their Error prototype during worker thread
799799 * transfer), and arbitrary values.
800800 */
801- function describeError ( error : unknown ) : string {
801+ export function describeError (
802+ error : unknown ,
803+ seen = new WeakSet < object > ( ) ,
804+ depth = 0
805+ ) : string {
806+ if ( depth > 10 ) {
807+ return '[Max error cause depth exceeded]' ;
808+ }
802809 if ( error instanceof Error ) {
803- return error . message ;
810+ return (
811+ error . message ||
812+ describeErrorObject ( error , seen , depth , {
813+ suppressGenericErrorName : true ,
814+ } )
815+ ) ;
804816 }
805817 if ( error && typeof error === 'object' ) {
806- // Comlink-serialized errors arrive as plain objects like
807- // { name: 'ErrnoError', errno: 20 } with no .message.
808- const parts = [ ] ;
809- const obj = error as Record < string , unknown > ;
810- if ( obj [ 'name' ] ) {
811- parts . push ( String ( obj [ 'name' ] ) ) ;
812- }
813- if ( obj [ 'message' ] ) {
814- parts . push ( String ( obj [ 'message' ] ) ) ;
815- }
816- if ( obj [ 'errno' ] !== undefined ) {
817- parts . push ( `errno: ${ obj [ 'errno' ] } ` ) ;
818- }
819- if ( parts . length > 0 ) {
820- return parts . join ( ' — ' ) ;
821- }
822- // Last resort: JSON-serialize the object so we at least see
823- // what fields it has.
824- try {
825- return JSON . stringify ( error ) ;
826- } catch {
827- return String ( error ) ;
828- }
818+ return describeErrorObject (
819+ error as Record < string , unknown > ,
820+ seen ,
821+ depth
822+ ) ;
829823 }
830824 return String ( error ) ;
831825}
832826
827+ function describeErrorObject (
828+ error : Record < string , unknown > ,
829+ seen : WeakSet < object > ,
830+ depth : number ,
831+ options : { suppressGenericErrorName ?: boolean } = { }
832+ ) : string {
833+ if ( seen . has ( error ) ) {
834+ return '[Circular error cause]' ;
835+ }
836+ seen . add ( error ) ;
837+
838+ // Comlink-serialized errors arrive as plain objects like
839+ // { name: 'ErrnoError', errno: 20 } with no .message.
840+ const parts = [ ] ;
841+ if (
842+ error [ 'name' ] &&
843+ ! ( options . suppressGenericErrorName && error [ 'name' ] === 'Error' )
844+ ) {
845+ parts . push ( String ( error [ 'name' ] ) ) ;
846+ }
847+ if ( error [ 'message' ] ) {
848+ parts . push ( String ( error [ 'message' ] ) ) ;
849+ }
850+ if ( error [ 'cause' ] ) {
851+ parts . push (
852+ `caused by: ${ describeError ( error [ 'cause' ] , seen , depth + 1 ) } `
853+ ) ;
854+ }
855+ if ( error [ 'errno' ] !== undefined ) {
856+ parts . push ( `errno: ${ error [ 'errno' ] } ` ) ;
857+ }
858+ if ( parts . length > 0 ) {
859+ return parts . join ( ' — ' ) ;
860+ }
861+ // Last resort: JSON-serialize the object so we at least see
862+ // what fields it has.
863+ try {
864+ return JSON . stringify ( error ) ;
865+ } catch {
866+ return String ( error ) ;
867+ }
868+ }
869+
833870function getMountForVfsPath (
834871 mounts : Mount [ ] ,
835872 vfsPath : string
0 commit comments