{"record":{"id":"e89698cbfe652436","repo":"facebook/docusaurus","slug":"error-loading-json-stringify-val-htmltag-t","errorCode":null,"errorMessage":"Error loading ${JSON.stringify(val)}, \"${htmlTag.tagName}\" is not a valid HTML tag. Either use a valid \"tagName\" or set \"customElement: true\".","messagePattern":"Error loading (.+?), \"(.+?)\" is not a valid HTML tag\\. Either use a valid \"tagName\" or set \"customElement: true\"\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/docusaurus/src/server/htmlTags.ts","lineNumber":37,"sourceCode":"\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    );\n  }\n}\n\nfunction hashRouterAbsoluteToRelativeTagAttribute(\n  name: string,\n  value: string,\n): string {\n  if ((name === 'src' || name === 'href') && value.startsWith('/')) {\n    return `.${value}`;\n  }\n  return value;\n}\n\nfunction htmlTagObjectToString({","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/facebook/docusaurus/blob/3f483e80e326cc646b54b83d564b3f0c4881b9a6/packages/docusaurus/src/server/htmlTags.ts#L19-L55","documentation":"Thrown by `assertIsHtmlTagObject` when the object has a string `tagName` but that name is not in Docusaurus's allowlist of known HTML tags, and `customElement: true` is not set. This guards against typos and encourages explicit opt-in for custom element names (e.g. web components).","triggerScenarios":"Using a `tagName` that is neither a standard HTML element nor flagged as custom — for instance `{tagName: 'my-widget'}` without `customElement: true`. The check at htmlTags.ts:37-44 fires when `!htmlTag.customElement && !(htmlTags as string[]).includes(htmlTag.tagName)`.","commonSituations":"Registering a web component / custom element in `headTags`; a typo like `'scritp'`; using a framework-specific tag (e.g. an Astro/island component name) without opting in.","solutions":["Fix typos in the `tagName` to match a standard HTML element.","For legitimate custom elements, add `customElement: true` to the tag object.","Confirm the tag name against the HTML spec / your web component definition."],"exampleFix":"// before\nheadTags: [{tagName: 'my-analytics', attributes: {id: 'x'}}],\n// after\nheadTags: [{tagName: 'my-analytics', attributes: {id: 'x'}, customElement: true}],","handlingStrategy":"validation","validationCode":"const CUSTOM = new Set(['my-widget','my-analytics']);\nfunction validateTag(t: HtmlTagObject) {\n  if (!t.customElement && !KNOWN_HTML_TAGS.has(t.tagName) && !CUSTOM.has(t.tagName)) {\n    throw new Error(`Unknown tagName ${t.tagName}; set customElement: true if intentional`);\n  }\n}","typeGuard":"function isAllowedTagName(tag: {tagName: string; customElement?: boolean}): boolean {\n  return Boolean(tag.customElement) || KNOWN_HTML_TAGS.has(tag.tagName);\n}","tryCatchPattern":null,"preventionTips":["Set `customElement: true` for any non-standard tag name intentionally.","Double-check tag names against the HTML spec when copy-pasting.","Centralize custom-element allowlists per project."],"tags":["config","html-tags","custom-elements","validation"],"backgroundTag":null,"analyzedSha":"3f483e80e326cc646b54b83d564b3f0c4881b9a6","analyzedAt":"2026-08-12T13:25:04.382Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}