mermaid-js/mermaid · error

Nodes and querySelector are both undefined

Error message

Nodes and querySelector are both undefined

What it means

runThrowsErrors() (the core of mermaid.run) needs a source of elements: either an explicit `nodes` array or a CSS `querySelector` matched against the document. If both are undefined it cannot find any diagrams and throws immediately. The public run() default is `querySelector: '.mermaid'`, so reaching this means the caller explicitly passed options that overrode the default with undefined.

Source

Thrown at packages/mermaid/src/mermaid.ts:158

  }
};

const runThrowsErrors = async function (
  { postRenderCallback, querySelector, nodes }: Omit<RunOptions, 'suppressErrors'> = {
    querySelector: '.mermaid',
  }
) {
  const conf = mermaidAPI.getConfig();

  log.debug(`${!postRenderCallback ? 'No ' : ''}Callback function found`);

  let nodesToProcess: ArrayLike<HTMLElement>;
  if (nodes) {
    nodesToProcess = nodes;
  } else if (querySelector) {
    nodesToProcess = document.querySelectorAll(querySelector);
  } else {
    throw new Error('Nodes and querySelector are both undefined');
  }

  log.debug(`Found ${nodesToProcess.length} diagrams`);
  if (conf?.startOnLoad !== undefined) {
    log.debug('Start On Load: ' + conf?.startOnLoad);
    mermaidAPI.updateSiteConfig({ startOnLoad: conf?.startOnLoad });
  }

  // generate the id of the diagram
  const idGenerator = new utils.InitIDGenerator(conf.deterministicIds, conf.deterministicIDSeed);

  let txt: string;
  const errors: DetailedError[] = [];

  // element is the current div with mermaid class
  // eslint-disable-next-line unicorn/prefer-spread
  for (const element of Array.from(nodesToProcess)) {
    log.info('Rendering diagram: ' + element.id);

View on GitHub (pinned to d93e9c88c0)

Solutions

  1. Pass a selector: mermaid.run({ querySelector: '.mermaid' }).
  2. Or pass explicit nodes: mermaid.run({ nodes: document.querySelectorAll('.mermaid') }).
  3. When building options conditionally, fall back to '.mermaid' if your value is falsy.
  4. Avoid calling runThrowsErrors directly; use the public run() which defaults the selector.

Example fix

// before — explicit undefined overrides the default
const opts = someCond ? { nodes: els } : { querySelector: undefined };
await mermaid.run(opts);

// after — guarantee a selector
const opts = someCond ? { nodes: els } : { querySelector: '.mermaid' };
await mermaid.run(opts);
Defensive patterns

Strategy: validation

Validate before calling

// Guarantee a node source before calling run
function runWith(opts = {}) {
  if (!opts.nodes && !opts.querySelector) {
    opts.querySelector = '.mermaid';
  }
  return mermaid.run(opts);
}

Type guard

const hasNodeSource = (o): o is { nodes: ArrayLike<HTMLElement> } | { querySelector: string } =>
  (o?.nodes != null && o.nodes.length >= 0) || typeof o?.querySelector === 'string' && o.querySelector.length > 0;

Try / catch

try {
  await mermaid.run(opts);
} catch (e) {
  if (e instanceof Error && /Nodes and querySelector are both undefined/.test(e.message)) {
    await mermaid.run({ ...opts, querySelector: '.mermaid' });
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling mermaid.run({}) or mermaid.run({ nodes: undefined, querySelector: undefined }) so the destructured options lose the '.mermaid' default; passing a falsy querySelector assuming a default still applies; calling runThrowsErrors() directly with no args in a non-DOM context.

Common situations: Constructing options dynamically and accidentally setting querySelector to undefined/null/empty; SSR or test environments where document is absent and callers pass nothing; refactors that build the options object conditionally and omit the selector.

Related errors


AI-assisted analysis of mermaid-js/mermaid@d93e9c88c0 (2026-08-12). Data as JSON: /api/errors/ae2f4026250fc1ba. Report an issue: GitHub.