{"record":{"id":"4f52f548f47ab3f3","repo":"facebook/docusaurus","slug":"val-is-not-a-valid-html-tag-object","errorCode":null,"errorMessage":"\"${val}\" is not a valid HTML tag object.","messagePattern":"\"(.+?)\" is not a valid HTML tag object\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/docusaurus/src/server/htmlTags.ts","lineNumber":23,"sourceCode":" * LICENSE file in the root directory of this source tree.\n */\n\nimport _ from 'lodash';\nimport htmlTags from 'html-tags';\nimport voidHtmlTags from 'html-tags/void';\nimport escapeHTML from 'escape-html';\nimport type {\n  Props,\n  HtmlTagObject,\n  HtmlTags,\n  LoadedPlugin,\n  RouterType,\n} from '@docusaurus/types';\n\n// TODO this should be done at config validation time, not here\nfunction assertIsHtmlTagObject(val: unknown): asserts val is HtmlTagObject {\n  if (typeof val !== 'object' || !val) {\n    throw new Error(`\"${val}\" is not a valid HTML tag object.`);\n  }\n  const htmlTag = val as HtmlTagObject;\n  if (typeof htmlTag.tagName !== 'string') {\n    throw new Error(\n      `${JSON.stringify(\n        val,\n      )} is not a valid HTML tag object. \"tagName\" must be defined as a string.`,\n    );\n  }\n  if (\n    !htmlTag.customElement &&\n    !(htmlTags as string[]).includes(htmlTag.tagName)\n  ) {\n    throw new Error(\n      `Error loading ${JSON.stringify(val)}, \"${\n        htmlTag.tagName\n      }\" is not a valid HTML tag. Either use a valid \"tagName\" or set \"customElement: true\".`,\n    );","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/facebook/docusaurus/blob/3f483e80e326cc646b54b83d564b3f0c4881b9a6/packages/docusaurus/src/server/htmlTags.ts#L5-L41","documentation":"Thrown by the internal `assertIsHtmlTagObject` guard when a value used as an HTML tag (in `headTags`/`scripts`/`stylesheets`) is not a non-null object. Docusaurus expects each tag entry to be either a string or an `HtmlTagObject`; a primitive/null/undefined fails this first check. The TODO in source notes this should ideally run at config validation time.","triggerScenarios":"Passing `null`, `undefined`, a number, or a boolean into one of the `headTags`, `scripts`, or `stylesheets` arrays in docusaurus.config.js or a plugin's injected tags. The `typeof val !== 'object' || !val` branch at htmlTags.ts:23 catches every non-object.","commonSituations":"Returning `undefined` from a plugin's `injectHtmlTags` hook due to a conditional bug; spreading a possibly-undefined array of tags; a typo producing `0` or `false` instead of a tag object.","solutions":["Ensure every entry in your tags arrays is either a string or a full `HtmlTagObject` (`{tagName, attributes, innerHTML}`).","Filter out falsy values before returning: `tags.filter(Boolean)`.","Audit plugin `injectHtmlTags`/`injectStyles`/`injectScripts` return values for conditional `undefined`."],"exampleFix":"// before\nheadTags: [shouldAddMeta && {tagName: 'meta', attributes: {...}}],\n// after\nheadTags: [shouldAddMeta && {tagName: 'meta', attributes: {...}}].filter(Boolean),","handlingStrategy":"validation","validationCode":"function sanitizeTags(tags: unknown[]): HtmlTagObject[] {\n  return tags.filter((t): t is HtmlTagObject =>\n    typeof t === 'string' || (typeof t === 'object' && t !== null));\n}","typeGuard":"function isHtmlTagObject(v: unknown): v is {tagName: string; attributes?: Record<string,string>; innerHTML?: string} {\n  return typeof v === 'object' && v !== null;\n}","tryCatchPattern":null,"preventionTips":["Always `.filter(Boolean)` before returning tag arrays from plugins.","Type plugin `injectHtmlTags` returns as `HtmlTags`.","Avoid conditional `&&` shorthand inside arrays without filtering."],"tags":["config","html-tags","validation"],"backgroundTag":null,"analyzedSha":"3f483e80e326cc646b54b83d564b3f0c4881b9a6","analyzedAt":"2026-08-12T13:25:04.382Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}