getOverflowAncestors calls isOverflowElement(null) when the node's document has no <body>
Describe the bug
In @floating-ui/utils (verified in 0.2.10 and in the current 0.2.12 dist) getNearestOverflowAncestor returns (node.ownerDocument || node).body. When the node's document has no <body> at that moment (body removed, element inside a <template> content document, XML document) that value is null.
getOverflowAncestors then computes isBody as scrollableAncestor === node.ownerDocument?.body, which is null === null → true, takes the body branch and calls isOverflowElement(null). That calls getComputedStyle(null) and throws:
TypeError: Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'.
@floating-ui/utils getComputedStyle floating-ui.utils.dom.mjs
@floating-ui/utils isOverflowElement floating-ui.utils.dom.mjs
@floating-ui/utils getOverflowAncestors floating-ui.utils.dom.mjs
@floating-ui/dom getClippingElementAncestors floating-ui.dom.mjs
@floating-ui/dom getClippingRect floating-ui.dom.mjs
@floating-ui/core detectOverflow floating-ui.core.mjs
@floating-ui/core flip / shift middleware floating-ui.core.mjs
@floating-ui/core computePosition floating-ui.core.mjsBecause computePosition is async, and @floating-ui/vue's update() chains .then(...) with no .catch, this surfaces as an unhandled promise rejection that application code cannot intercept.
Reproduction (no framework)
import { computePosition, flip } from '@floating-ui/dom';
const reference = document.createElement('div');
const floating = document.createElement('div');
document.body.append(reference, floating);
document.body.remove(); // any body-less document reproduces it
await computePosition(reference, floating, { middleware: [flip()] });
// TypeError: Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'.offset() alone does not throw; any middleware that calls detectOverflow does.
Expected behavior
Either treat a missing body like the document element (fall back to getDocumentElement(node) in getNearestOverflowAncestor), or skip the isOverflowElement call when scrollableAncestor is null in getOverflowAncestors. Either way computePosition should resolve rather than reject.
Context
Seen in production through @vuepic/vue-datepicker (which runs autoUpdate via @floating-ui/vue): roughly 215 events across 61 users in 90 days, Chrome 149 to 153 on Windows. Previous issues with the same message had different causes: #2691 (component instance not unwrapped in production builds) and #1840 (null refs).
Versions
@floating-ui/utils0.2.10 (also reproduced by reading 0.2.12 dist)@floating-ui/dom1.7.5,@floating-ui/core1.7.4,@floating-ui/vue1.1.10
Source: floating-ui/floating-ui