diff --git a/changelog.d/9933-util-inspect-lazy-error-stack.md b/changelog.d/9933-util-inspect-lazy-error-stack.md new file mode 100644 index 0000000000..71a4dbb821 --- /dev/null +++ b/changelog.d/9933-util-inspect-lazy-error-stack.md @@ -0,0 +1,5 @@ +### Fixed + +- Materialize lazy Error stacks when `util.inspect()` formats an Error with a + `cause` or an `AggregateError` with `errors`, preserving Node-compatible + stack/body layout. diff --git a/crates/perry-runtime/src/builtins/formatting/errors.rs b/crates/perry-runtime/src/builtins/formatting/errors.rs index 76eaa7543c..d940f895d9 100644 --- a/crates/perry-runtime/src/builtins/formatting/errors.rs +++ b/crates/perry-runtime/src/builtins/formatting/errors.rs @@ -49,7 +49,13 @@ unsafe fn format_error_headline(error_ptr: *const crate::error::ErrorHeader) -> } unsafe fn format_error_stack_frame(error_ptr: *const crate::error::ErrorHeader) -> Option { - let stack = string_header_to_string((*error_ptr).stack, ""); + // #9486 made Error stacks lazy: `ErrorHeader.stack` stays null until the + // first observable read materializes the captured frame payload. Inspect + // is one of those reads. Looking at the slot directly made Errors with a + // cause or AggregateError.errors print a standalone `{` where Node keeps + // it attached to the final stack line. + let stack_ptr = crate::error::materialize_error_stack(error_ptr.cast_mut()); + let stack = string_header_to_string(stack_ptr, ""); stack .lines() .skip(1)