Automattic/harper · error · TypeError

Expected a DOM Element

Error message

Expected a DOM Element

What it means

lint-framework's Highlights.isContainingBlock computes computed styles to detect containing blocks; it begins by asserting the argument is a DOM Element and throws TypeError('Expected a DOM Element') otherwise. This is called from getInitialContainingRect while mapping lint spans to on-screen rectangles, so it fires when highlight bookkeeping feeds a non-Element into containing-block resolution.

Source

Thrown at packages/lint-framework/src/lint/Highlights.ts:287

		if (isContainingBlock(node)) {
			return node.getBoundingClientRect();
		}
		node = node.parentElement;
	}

	return null;
}
/**
 * Determines whether a given element would form the containing block
 * for a descendant with `position: fixed`, based on CSS transforms,
 * filters, containment, container queries, will-change, and
 * content-visibility.
 *
 * Logs the element and the precise reason it qualifies.
 */
function isContainingBlock(el: Element): boolean {
	if (!(el instanceof Element)) {
		throw new TypeError('Expected a DOM Element');
	}

	const style = window.getComputedStyle(el);

	const filter = style.getPropertyValue('filter');
	if (filter !== 'none') {
		return true;
	}

	const backdrop = style.getPropertyValue('backdrop-filter');
	if (backdrop !== 'none') {
		return true;
	}

	const transform = style.getPropertyValue('transform');
	if (transform !== 'none') {
		return true;
	}

View on GitHub (pinned to 5fe7d5ab76)

Solutions

  1. Guard the call site: skip isContainingBlock when the candidate is null or its nodeType is not 1 (Node.ELEMENT_NODE).
  2. Update lint-framework to a version that handles a missing parent before calling isContainingBlock.
  3. Check that the element you highlight inside is attached to the document and is an Element (nodeType 1).
  4. In tests, provide a realistic DOM (jsdom with parent traversal) or mock getInitialContainingRect.

Example fix

// before (SSR)
const highlights = new Highlights(editorEl); // runs during server render
// after
import { browser } from '$app/environment';
if (browser) {
  const highlights = new Highlights(editorEl);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof window === 'undefined') {
  throw new Error('Highlights requires a browser DOM environment');
}

Type guard

function isElement(v: unknown): v is Element {
  return typeof Element !== 'undefined' && v instanceof Element;
}

Try / catch

try {
  const rect = highlights.getInitialContainingRect(lint);
} catch (e) {
  if (e instanceof TypeError && e.message === 'Expected a DOM Element') {
    console.error('Highlighter ran outside a browser DOM; skip highlight setup.');
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing a non-Element into isContainingBlock while a DOM is present: el.parentElement returning null as the upward walk reaches the document root, a text or comment node arriving from a shadow-DOM or custom rendering path, or a detached placeholder swapped in mid-highlight. A DOM is required for this error to be reachable at all — the check is `el instanceof Element`, so an environment with no global Element (bare Node, SSR without DOM shims) raises ReferenceError: Element is not defined on that same line instead.

Common situations: Frameworks replacing nodes mid-highlight so parentElement is null by the time the lookup runs; shadow-DOM or custom renderers handing the highlighter a text node; jsdom setups that define Element but not the parent chain the walk expects.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of Automattic/harper@5fe7d5ab76 (2026-09-06). Data as JSON: /api/errors/57c27067ed60784e. Report an issue: GitHub.