Skip to content

Wrong stack trace in case of error in the router component #4250

@slenz

Description

@slenz
  • Framework7 version: 8.3.1
  • Platform and Target: all

Describe the bug

When error happens in the custom router component during the loading of the component (for example Type Error happens), then stack information of the original error is lost.
I attached the "unhandledrejection" handler to "window" and expect to get correct stack trace but stack trace started at the "component-loader.js" itself.
I have proposition how to make a workaround for this problem, please look at the patch below in the "Proposition" section.

To Reproduce

  1. Make some typing error in the some page (i call not existed function "ttt()" in the example below).
  2. Attach ("unhandledrejection" event handler to window.
window.addEventListener("unhandledrejection", function (e) {
  console.log("window.unhandledrejection", e);
};
  1. Navigate to that page.
  2. See the console log, it will show something like this. No stack of original error.
window.unhandledrejection: Error: ReferenceError: ttt is not defined
    at component-loader.js:121:17

Expected behavior

Stack with correct error location.

window.unhandledrejection: ReferenceError: ttt is not defined
    at Component.renderFunction (home.jsx:17:21)
    at Component.render (component-class.js:361:17)
    at component-class.js:173:27

Actual Behavior

Stack with incorrect error location.

window.unhandledrejection: Error: ReferenceError: ttt is not defined
    at component-loader.js:121:17

Proposition

This happens because stack is lost when error re-thrown in the modules/router/component-loader.js.
I propose to add the original error object to the new Error thrown (for example as "cause" object).
This will allow us to check for the "cause" object in the Error and get the correct stack frame in the "unhandledrejection" handler.
I propose to make small patch in the modules/router/async-component.js and modules/router/component-loader.js:

--- async-component.js.old	2024-02-05 13:28:24.000000000 +0300
+++ async-component.js	2024-02-16 12:49:17.980356045 +0300
@@ -7,7 +7,7 @@
       })
       .catch((err) => {
         reject();
-        throw new Error(err);
+        throw new Error(err, {cause: err});
       });
   }
   if (component instanceof Promise) {

--- component-loader.js.old     2024-02-05 13:28:24.000000000 +0300
+++ component-loader.js 2024-02-16 12:47:39.380287616 +0300
@@ -117,7 +117,7 @@
           })
           .catch((err) => {
             reject(err);
-            throw new Error(err);
+            throw new Error(err, {cause: err});
           });
       }
       let cachedComponent;

Then in the "unhandledrejection" handler we can check for the "cause" and it will contain the original error's stack.

window.addEventListener("unhandledrejection", function (e) {
    if (e.reason.cause) {
        console.log("window.unhandledrejection:", e.reason.cause);
    } else {
        console.log("window.unhandledrejection:", e.reason);
    }
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions