Skip to content

Report frontend errors to a Netlify function - #799

Merged
apbendi merged 3 commits into
masterfrom
client-error-reporting
Nov 11, 2026
Merged

apbendi merged 3 commits into
masterfrom
client-error-reporting

Conversation

@apbendi

@apbendi apbendi commented Nov 10, 2026

Copy link
Copy Markdown
Member

The app showed errors to the user and logged them to the browser console, but had no way to tell us about them. Errors caught by the global handlers (Vue's error handler, the error event and unhandledrejection) now go through one capture function, which reports the error and shows the notification as before. Some catch blocks that handle an error without showing it also report it, with a label naming the block.

Reports go to a new function at /api/client-error, which writes each one to the function's logs as a single line starting with "client_error ". A report holds the error's name, code, message and stack, the commit the app was built from, the route name, the network, the connected wallet's name and the locale. The function adds the browser and OS family from the user agent, and doesn't log the IP address.

Before a report is sent, the app removes addresses, keys, signatures and other long hex strings, ENS and other domain names, email addresses and amounts, and reduces URLs other than the app's own to their host. The function applies the same rules again, since anyone can call it. Both are tested against one shared set of examples.

Only production builds send reports. User rejections, messages the app already ignores and errors that come only from extensions or injected wallet scripts are left out. The same error is sent once per page load, and no more than 10 reports per page load. The function accepts requests up to 16 KB and is rate limited per IP.

Reporting is on by default and can be turned off with a new toggle in the footer's Settings section, in all three locales. Clearing local storage keeps that choice. The privacy policy now describes the reports and how long they're kept.

The invalid private key error no longer repeats the key that was entered, so it can't reach logs or reports. The list of messages that aren't worth showing moved to errors.ts so the reporter can share it. Prettier now formats .mjs files like .js files.

@netlify

netlify Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for jolly-shaw-20fe62 ready!

Name Link
🔨 Latest commit f50cb5f
🔍 Latest deploy log https://app.netlify.com/projects/jolly-shaw-20fe62/deploys/6af492d9ac094a2331512271
😎 Deploy Preview https://deploy-preview-799--jolly-shaw-20fe62.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@apbendi
apbendi requested a review from mikeghen November 10, 2026 17:54
The app showed errors to the user and logged them to the browser
console, but had no way to tell us about them. Errors caught by the
global handlers (Vue's error handler, the error event and
unhandledrejection) now go through one capture function, which reports
the error and shows the notification as before. Some catch blocks that
handle an error without showing it also report it, with a label naming
the block.

Reports go to a new function at /api/client-error, which writes each
one to the function's logs as a single line starting with
"client_error ". A report holds the error's name, code, message and
stack, the commit the app was built from, the route name, the network,
the connected wallet's name and the locale. The function adds the
browser and OS family from the user agent, and doesn't log the IP
address.

Before a report is sent, the app removes addresses, keys, signatures
and other long hex strings, ENS and other domain names, email
addresses and amounts, and reduces URLs other than the app's own to
their host. The function applies the same rules again, since anyone
can call it. Both are tested against one shared set of examples.

Only production builds send reports. User rejections, messages the app
already ignores and errors that come only from extensions or injected
wallet scripts are left out. The same error is sent once per page
load, and no more than 10 reports per page load. The function accepts
requests up to 16 KB and is rate limited per IP.

Reporting is on by default and can be turned off with a new toggle in
the footer's Settings section, in all three locales. Clearing local
storage keeps that choice. The privacy policy now describes the
reports and how long they're kept.

The invalid private key error no longer repeats the key that was
entered, so it can't reach logs or reports. The list of messages that
aren't worth showing moved to errors.ts so the reporter can share it.
Prettier now formats .mjs files like .js files.
ENS names can contain letters in any script, emoji, and symbols such as
$ and '. The scrubbers now find names by their dots rather than by the
characters they contain. A run of text between spaces, quotes, brackets
and similar delimiters that contains a dot becomes <name>, unless its
last label starts with a digit, as in a decimal.

Some umbra-js errors quote the recipient the user entered, which might
not contain a dot. The scrubbers now replace the recipient in those
messages first.

Unchanged text between matches is kept together, so later rules, such
as the one for amounts with thousands separators, still see it whole.

The frontend and the function apply the same rules. The shared examples
add names in several scripts, emoji sequences, quoted names and the
umbra-js messages, and a new test follows two of these messages from the
reporter to the function's log.
The reporter leaves out errors whose stack frames all come from other
origins, since the browser's error and unhandledrejection events also
see errors from browser extensions and injected wallet scripts that
have nothing to do with the app. Wallets often reject a request with an
error created in their own script, and ethers passes it on unchanged,
so the error has only the wallet's frames even when the app made the
request.

Apply that filter only to errors from the error and unhandledrejection
events. Errors from Vue's error handler and from catch blocks that call
reportError come from the app's own operations, so they're reported
whatever their frames. User rejections and ignored messages are still
left out for every source.

Tests cover each source, and a wallet error passed on by ethers'
Web3Provider.
@apbendi
apbendi force-pushed the client-error-reporting branch from ab97fa0 to f50cb5f Compare November 11, 2026 16:07
@github-actions

Copy link
Copy Markdown

Coverage after merging client-error-reporting into master will be

88.10%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
contracts-periphery/src
   UmbraBatchSend.sol100%100%100%100%
   UniswapWithdrawHook.sol100%100%100%100%
umbra-js/src
   ethers.ts82.22%100%63.64%100%
umbra-js/src/classes
   KeyPair.ts98.54%97.62%100%98.75%248, 250
   RandomNumber.ts100%100%100%100%
   StealthKeyRegistry.ts100%100%100%100%
   TxHistoryProvider.ts81.48%65%80%93.10%12, 17, 20, 23, 29, 43, 48, 54, 8
   Umbra.ts82.65%72.22%93.02%87.59%167–168, 189, 262–263, 331, 349–352, 388–389, 413, 428, 431, 438–440, 442, 449, 452, 452, 452–453, 460, 460, 460, 460, 460–461, 463–464, 494, 494, 494, 496, 496, 496, 498, 498, 498, 498–499, 499, 499–501, 517–518, 583–585, 62, 62, 626, 681, 685, 731, 744–745, 755–756, 770, 778–779, 809–810, 885–886, 889, 892–893, 899–901, 908, 908, 908–911, 946, 948–949, 949–950
umbra-js/src/utils
   constants.ts100%100%100%100%
   sharedSecret.ts83.05%72.73%100%87.88%24, 27–28, 42–43, 47–48, 62, 62–63
   utils.ts77.72%71.25%87.50%81.65%109–110, 1173–1174, 1186–1187, 135, 143, 143, 159–161, 163–165, 171, 193–194, 220–222, 222, 222–223, 223, 223, 304, 307, 312, 316, 320, 330, 342–347, 349–356, 356, 356, 356, 356, 356, 356–357, 359, 369, 400–402, 408, 418–419, 426–427, 434, 444, 444–445, 445–446, 451, 481–482, 491–492, 512–514, 527–528, 554–556, 565, 638, 645, 645–646, 646, 646–647, 647, 649, 653, 716, 718–719, 727–728, 746, 777, 799–801, 804, 814, 826, 826–827, 832, 834–835, 846–850, 863, 865, 916–917, 917, 917–919, 919, 919, 930
umbra-js/test
   testPrivateKeys.ts100%100%100%100%
   utils.ts100%100%100%100%

@apbendi
apbendi merged commit 013daea into master Nov 11, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants