Tracker: click handler assumes e.target is an Element, throws on clicks dispatched on document
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- typescript
- Domain
- analytics
Research direction
Start at the click handler in src/tracker/index.ts around lines 348–351 and reproduce the issue with document.dispatchEvent(new MouseEvent('click', { bubbles: true })). Verify that clicks whose targets are not Elements no longer throw, while ordinary element clicks still reach the tracker's event lookup and tracking behavior.
Written by the indexing model from the issue text.
Description
Summary
The tracker's click handler casts e.target to Element and immediately calls .closest() on it. Any click event whose target is not an Element — most easily document itself — throws TypeError: e.target.closest is not a function from inside the listener.
const onClick = (e: MouseEvent) => {
const el = e.target as Element;
const eventEl = el.closest(`[${eventNameAttribute}]`);
The as Element cast is the assumption that breaks: the listener is registered on document in the capture phase, so it receives every click in the page — including ones dispatched programmatically on document or on any other non-Element node.
Steps to reproduce
On any page with the tracker loaded, in the console:
document.dispatchEvent(new MouseEvent('click', { bubbles: true }));
Result (captured on a live page running the tracker, window.onerror):
{
"message": "Uncaught TypeError: e.target.closest is not a function",
"filename": "https://<host>/script.js",
"lineno": 1,
"colno": 2152
}
typeof document.closest is "undefined" — Document does not implement Element, so there is nothing to call.
Why it matters in practice
We did not reproduce this synthetically first — it arrived from a real visitor (Chrome 151 / macOS) and landed in our error tracker attributed to our own application, because the tracker is proxied first-party so ad blockers don't drop it. Browser extensions and third-party widgets dispatch synthetic clicks on document routinely, so this surfaces as unexplained noise in the host site's error reporting rather than anything the site owner can act on.
Functional impact is small — an exception in one listener doesn't stop the others, so pageview beacons are unaffected and only that one click's data-umami-event tracking is lost — but the error is unavoidable and unsilenceable from the host page.
Suggested fix
Guard instead of cast:
const onClick = (e: MouseEvent) => {
const el = e.target;
if (!(el instanceof Element)) return;
const eventEl = el.closest(`[${eventNameAttribute}]`);
instanceof Element also handles text nodes and window targets, not just document.
Environment
- Tracker served by umami 3.3.0 (
ghcr.io/umami-software/umami:postgresql-latest), self-hosted, PostgreSQL - Code path unchanged on
master(3.3.1) as of ca661c7 - Chrome 151, macOS
- Dominant language
- TypeScript
- Stars
- 38.9k
- Forks
- 8.1k
- Avg merge
- 5d 19h
- Merged PRs (30d)
- 21
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from umami-software/umami
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
umami-software/umami#4552 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
umami-software/umami#4526 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
umami-software/umami#4491 ·
-
Difficulty 3/5 1-2 days Newbie friendliness 68/100
umami-software/umami#4555 · 1 reaction ·
-
Difficulty 5/5 Over a week Newbie friendliness 30/100
umami-software/umami#4549 ·
All issues in umami-software/umami
Similar issues
-
calcite-components needs triage refactor
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
Esri/calcite-design-system#15203 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 91/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 95/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Automattic/studio#4908 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 90/100