getOverflowAncestors calls isOverflowElement(null) when the node's document has no <body>

Author: psnoonanCreated Sep 17, 2026Updated Sep 17, 2026

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 === nulltrue, 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.mjs

Because 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)

javascript
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/utils 0.2.10 (also reproduced by reading 0.2.12 dist)
  • @floating-ui/dom 1.7.5, @floating-ui/core 1.7.4, @floating-ui/vue 1.1.10