facebook/docusaurus · error · Error

${JSON.stringify(val)} is not a valid HTML tag object. "tagN

Error message

${JSON.stringify(val)} is not a valid HTML tag object. "tagName" must be defined as a string.

What it means

Thrown by `assertIsHtmlTagObject` when the value IS an object but its `tagName` property is missing or not a string. This is the second-stage check after the object-shape check; it pins the failure precisely to the `tagName` field.

Source

Thrown at packages/docusaurus/src/server/htmlTags.ts:27

import htmlTags from 'html-tags';
import voidHtmlTags from 'html-tags/void';
import escapeHTML from 'escape-html';
import type {
  Props,
  HtmlTagObject,
  HtmlTags,
  LoadedPlugin,
  RouterType,
} from '@docusaurus/types';

// TODO this should be done at config validation time, not here
function assertIsHtmlTagObject(val: unknown): asserts val is HtmlTagObject {
  if (typeof val !== 'object' || !val) {
    throw new Error(`"${val}" is not a valid HTML tag object.`);
  }
  const htmlTag = val as HtmlTagObject;
  if (typeof htmlTag.tagName !== 'string') {
    throw new Error(
      `${JSON.stringify(
        val,
      )} is not a valid HTML tag object. "tagName" must be defined as a string.`,
    );
  }
  if (
    !htmlTag.customElement &&
    !(htmlTags as string[]).includes(htmlTag.tagName)
  ) {
    throw new Error(
      `Error loading ${JSON.stringify(val)}, "${
        htmlTag.tagName
      }" is not a valid HTML tag. Either use a valid "tagName" or set "customElement: true".`,
    );
  }
}

function hashRouterAbsoluteToRelativeTagAttribute(

View on GitHub (pinned to 3f483e80e3)

Solutions

  1. Add a string `tagName` to the object (e.g. `'meta'`, `'script'`, `'link'`).
  2. If constructing tags from data, validate `typeof obj.tagName === 'string'` before pushing into the array.
  3. Use the string-shorthand form (`'<meta .../>'`) instead of the object form for static tags.

Example fix

// before
scripts: [{attributes: {src: '/x.js'}}],
// after
scripts: [{tagName: 'script', attributes: {src: '/x.js'}}],
Defensive patterns

Strategy: type-guard

Validate before calling

function assertTagsHaveName(tags: unknown[]) {
  tags.forEach((t, i) => {
    if (typeof t === 'object' && t !== null && typeof (t as any).tagName !== 'string') {
      throw new Error(`Tag at index ${i} is missing a string tagName`);
    }
  });
}

Type guard

function isHtmlTagObject(v: unknown): v is {tagName: string; attributes?: Record<string,string>} {
  return typeof v === 'object' && v !== null && typeof (v as any).tagName === 'string';
}

Prevention

When it happens

Trigger: Passing an object like `{attributes: {...}}` or `{tagName: 123}` into a tags array — i.e. an object that survived the first guard but lacks a string `tagName`. Triggered at htmlTags.ts:27 when `typeof htmlTag.tagName !== 'string'`.

Common situations: Building a tag object dynamically and forgetting the `tagName` key; renaming `tagName` to `tag` or `name`; using a numeric or symbol tagName.

Related errors


AI-assisted analysis of facebook/docusaurus@3f483e80e3 (2026-08-12). Data as JSON: /api/errors/f182b4db8977e4d8. Report an issue: GitHub.