didi/DoKit · error · Error

Can't generate CSS selector for non-element node type.

Error message

Can't generate CSS selector for non-element node type.

What it means

Thrown by the CSS selector generator (a fork of the 'finder' library) when the node passed in is not an ELEMENT_NODE. The library builds a unique CSS selector by walking up the DOM from an element, so it can only operate on element nodes. Passing a text node, comment node, document, or document fragment triggers this immediately at the entry check.

Source

Thrown at Web/packages/core/src/common/js/finder/index.js:13

import cssesc from 'cssesc';

let Limit;
(function (Limit) {
  Limit[Limit.All = 0] = 'All';
  Limit[Limit.Two = 1] = 'Two';
  Limit[Limit.One = 2] = 'One';
}(Limit || (Limit = {})));
let config;
let rootDocument;
export default function (input, options) {
  if (input.nodeType !== Node.ELEMENT_NODE) {
    throw new Error('Can\'t generate CSS selector for non-element node type.');
  }
  if (input.tagName.toLowerCase() === 'html') {
    return 'html';
  }
  const defaults = {
    root: document.body,
    idName: name => true,
    className: (name, input) => true,
    tagName: name => true,
    attr: (name, value) => false,
    seedMinLength: 1,
    optimizedMinLength: 2,
    threshold: 1000,
  };
  config = Object.assign({}, defaults, options);
  rootDocument = findRootDocument(config.root, defaults);
  let path = bottomUpSearch(input, Limit.All, () => bottomUpSearch(input, Limit.Two, () => bottomUpSearch(input, Limit.One)));
  // console.log('bottomUpSearch:', path)

View on GitHub (pinned to 626827cddb)

Solutions

  1. Check node.nodeType === Node.ELEMENT_NODE (or node.nodeType === 1) before calling the finder
  2. Use element.children / parentElement instead of childNodes / parentNode when you need element-only traversal
  3. If the node is a text/comment node, pass its parentElement instead: node.parentElement
  4. Guard with instanceof: node instanceof Element (or the appropriate DOM class) before generating the selector

Example fix

// before
const selector = finder(node, { root: document.body });

// after
if (node && node.nodeType === Node.ELEMENT_NODE) {
  const selector = finder(node, { root: document.body });
} else if (node && node.parentElement) {
  const selector = finder(node.parentElement, { root: document.body });
}
Defensive patterns

Strategy: type-guard

Validate before calling

const isElement = (n) => n && n.nodeType === Node.ELEMENT_NODE;
if (!isElement(node)) node = node && node.parentElement;
if (isElement(node)) selector = finder(node, { root: document.body });

Type guard

function isElementNode(node) {
  return node instanceof Element;
}

Prevention

When it happens

Trigger: Calling the default export of finder/index.js with: a text node (nodeType 3) obtained via firstChild/childNodes[i], a comment node (nodeType 8), document (nodeType 9), documentFragment (nodeType 11), or the result of a DOM API that returns null/undefined coerced through a non-element accessor. Also happens when event.target is a text node (e.g. from a non-element context) and is fed directly to the finder.

Common situations: Recording user clicks for session replay or element-picker features: e.target is usually an element but can be a text node in some browsers/edge cases. Accessing parentNode of an element can return a document node. Using childNodes (returns all node types) instead of children (elements only).

Related errors


AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14). Data as JSON: /api/errors/0a10922583faa24d. Report an issue: GitHub.