diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index 5351390a8..f6b8b7e78 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -19,6 +19,7 @@
misc/dateUtils.js
misc/dbusUtils.js
misc/dependencies.js
+ misc/errorUtils.js
misc/extensionUtils.js
misc/fileUtils.js
misc/gnomeSession.js
diff --git a/js/misc/errorUtils.js b/js/misc/errorUtils.js
new file mode 100644
index 000000000..c2bc1c794
--- /dev/null
+++ b/js/misc/errorUtils.js
@@ -0,0 +1,62 @@
+// Common code for displaying errors to the user in various dialogs
+
+function formatSyntaxErrorLocation(error) {
+ const {fileName = '', lineNumber = 0, columnNumber = 0} = error;
+ return ` @ ${fileName}:${lineNumber}:${columnNumber}`;
+}
+
+function formatExceptionStack(error) {
+ const {stack} = error;
+ if (!stack)
+ return '\n\n(No stack trace)';
+
+ const indentedStack = stack.split('\n').map(line => ` ${line}`).join('\n');
+ return `\n\nStack trace:\n${indentedStack}`;
+}
+
+function formatExceptionWithCause(error, seenCauses) {
+ let fmt = formatExceptionStack(error);
+
+ const {cause} = error;
+ if (!cause)
+ return fmt;
+
+ fmt += `\nCaused by: ${cause}`;
+
+ if (cause !== null && typeof cause === 'object') {
+ if (seenCauses.has(cause))
+ return fmt; // avoid recursion
+ seenCauses.add(cause);
+
+ fmt += formatExceptionWithCause(cause, seenCauses);
+ }
+
+ return fmt;
+}
+
+/**
+ * Formats a thrown exception into a string, including the stack, taking the
+ * location where a SyntaxError was thrown into account.
+ *
+ * @param {Error} error The error to format
+ * @returns {string} The formatted string
+ */
+export function formatError(error) {
+ try {
+ let fmt = `${error}`;
+ if (error === null || typeof error !== 'object')
+ return fmt;
+
+ if (error instanceof SyntaxError) {
+ fmt += formatSyntaxErrorLocation(error);
+ fmt += formatExceptionStack(error);
+ return fmt;
+ }
+
+ const seenCauses = new Set([error]);
+ fmt += formatExceptionWithCause(error, seenCauses);
+ return fmt;
+ } catch (e) {
+ return `(could not display error: ${e})`;
+ }
+}