Bug description
In v6's event-delegation model, the single document-level mouseover listener throws TypeError: Cannot read properties of undefined (reading 'tooltipId') when an event's target is the document itself (not an Element).
This happens with synthetic / untrusted mouseover events, which are commonly dispatched directly on document by browser extensions, password managers, and accessibility/automation tooling. Real user hovers always target an Element, so this stays hidden in normal use but surfaces in production.
The crash is in resolveDataTooltipAnchor (src/utils/resolve-data-tooltip-anchor.ts):
function resolveDataTooltipAnchor(targetElement, tooltipId) {
let currentElement = targetElement; // = document
while (currentElement) {
if (currentElement.dataset.tooltipId === tooltipId) { // document.dataset is undefined -> throws
return currentElement;
}
currentElement = currentElement.parentElement;
}
return null;
}
document.dataset is undefined (only HTMLElement / SVGElement expose dataset), so reading .tooltipId from it throws.
The guard in resolveAnchorElementRef (src/components/Tooltip/use-tooltip-events.tsx) only checks targetElement?.isConnected, but document.isConnected === true, so document slips through into the data-tooltip-id branch. Note the anchorSelector branch is wrapped in a try/catch, but the dataTooltipId branch is not, so the common data-tooltip-id usage is unprotected.
Package version
6.0.5 (also confirmed in 6.0.0)
To reproduce
- Render a tooltip using the default attribute anchor:
<a data-tooltip-id="my-tip">hover me</a>
<Tooltip id="my-tip" content="hi" />
- Dispatch a synthetic mouseover on the document (this is what extensions/tooling do):
document.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
- The delegated listener runs
resolveDataTooltipAnchor(document, 'my-tip'), which does document.dataset.tooltipId and throws.
Expected behavior
The delegated mouseover/mouseout/etc. listeners should ignore non-Element targets (such as document) and not throw.
Actual behavior
Uncaught TypeError: Cannot read properties of undefined (reading 'tooltipId')
at resolveDataTooltipAnchor (resolve-data-tooltip-anchor.ts)
at resolveAnchorElementRef.current (use-tooltip-events.tsx)
at handler (use-tooltip-events.tsx)
at dispatch (event-delegation.ts)
The error escapes to window.onerror.
Suggested fix
Bail on non-Element targets at the guard, and/or harden the walk:
// use-tooltip-events.tsx - resolveAnchorElementRef.current
if (!(targetElement instanceof Element) || !targetElement.isConnected) {
return null;
}
// and/or resolve-data-tooltip-anchor.ts
while (currentElement) {
if (currentElement instanceof HTMLElement && currentElement.dataset.tooltipId === tooltipId) {
return currentElement;
}
currentElement = currentElement.parentElement;
}
Environment
- OS: Windows 10+
- Browser: Chrome 139
- React version: 19
- Framework: Next.js
- Rendering mode: client
Additional context
Caught via production error monitoring (Sentry): 26 occurrences across 26 unique users over 3 days. The event payload confirms isTrusted: false and target: [object HTMLDocument] on the mouseover event, consistent with a synthetic event dispatched on document rather than a real user hover.
Bug description
In v6's event-delegation model, the single document-level
mouseoverlistener throwsTypeError: Cannot read properties of undefined (reading 'tooltipId')when an event'stargetis thedocumentitself (not anElement).This happens with synthetic / untrusted
mouseoverevents, which are commonly dispatched directly ondocumentby browser extensions, password managers, and accessibility/automation tooling. Real user hovers always target anElement, so this stays hidden in normal use but surfaces in production.The crash is in
resolveDataTooltipAnchor(src/utils/resolve-data-tooltip-anchor.ts):document.datasetisundefined(onlyHTMLElement/SVGElementexposedataset), so reading.tooltipIdfrom it throws.The guard in
resolveAnchorElementRef(src/components/Tooltip/use-tooltip-events.tsx) only checkstargetElement?.isConnected, butdocument.isConnected === true, sodocumentslips through into thedata-tooltip-idbranch. Note theanchorSelectorbranch is wrapped in atry/catch, but thedataTooltipIdbranch is not, so the commondata-tooltip-idusage is unprotected.Package version
6.0.5 (also confirmed in 6.0.0)
To reproduce
resolveDataTooltipAnchor(document, 'my-tip'), which doesdocument.dataset.tooltipIdand throws.Expected behavior
The delegated
mouseover/mouseout/etc. listeners should ignore non-Elementtargets (such asdocument) and not throw.Actual behavior
The error escapes to
window.onerror.Suggested fix
Bail on non-
Elementtargets at the guard, and/or harden the walk:Environment
Additional context
Caught via production error monitoring (Sentry): 26 occurrences across 26 unique users over 3 days. The event payload confirms
isTrusted: falseandtarget: [object HTMLDocument]on themouseoverevent, consistent with a synthetic event dispatched ondocumentrather than a real user hover.