{"record":{"id":"610d7adffc00a047","repo":"withastro/astro","slug":"svg-file-does-not-contain-an-svg-element","errorCode":null,"errorMessage":"SVG file does not contain an <svg> element","messagePattern":"SVG file does not contain an <svg> element","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/astro/src/assets/svg/utils.ts","lineNumber":36,"sourceCode":"\tif (svgOptimizer) {\n\t\ttry {\n\t\t\tprocessedContents = await svgOptimizer.optimize(contents);\n\t\t} catch (cause) {\n\t\t\tthrow new AstroError(\n\t\t\t\t{\n\t\t\t\t\t...AstroErrorData.CannotOptimizeSvg,\n\t\t\t\t\tmessage: AstroErrorData.CannotOptimizeSvg.message(path, svgOptimizer.name),\n\t\t\t\t},\n\t\t\t\t{ cause },\n\t\t\t);\n\t\t}\n\t}\n\tconst root = parse(processedContents);\n\tconst svgNode = root.children.find(\n\t\t({ name, type }: { name: string; type: number }) => type === ELEMENT_NODE && name === 'svg',\n\t);\n\tif (!svgNode) {\n\t\tthrow new Error('SVG file does not contain an <svg> element');\n\t}\n\tconst { attributes, children } = svgNode;\n\tconst body = renderSync({ ...root, children });\n\n\t// Collect text content of <style> elements for head propagation and CSP hashing\n\tconst styles: string[] = [];\n\tfor (const child of children) {\n\t\tif (child.type === ELEMENT_NODE && child.name === 'style') {\n\t\t\tconst textContent = child.children\n\t\t\t\t?.filter((c: { type: number }) => c.type === TEXT_NODE)\n\t\t\t\t.map((c: { value: string }) => c.value)\n\t\t\t\t.join('');\n\t\t\tif (textContent) {\n\t\t\t\tstyles.push(textContent);\n\t\t\t}\n\t\t}\n\t}\n","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/assets/svg/utils.ts#L18-L54","documentation":"After optimization (or directly if no optimizer is configured), Astro parses the SVG with parse-html-parser and looks for the first element node named `svg`. If none is found it throws a plain Error (not an AstroError) with this message. This means the imported .svg file's root structure is not actually an <svg> document.","triggerScenarios":"ESM-importing a file whose extension is .svg but whose content is HTML, XML, plain text, an empty file, or an SVG wrapped in other elements (e.g. a full HTML document or a <!DOCTYPE html>). The check `type === ELEMENT_NODE && name === 'svg'` fails on the top-level children.","commonSituations":"An asset downloaded/exported as .svg that is actually HTML or an XML preamble without an <svg> root; an SVG fragment that begins with <defs> or <g> at top level; a file that was overwritten by a build step writing non-SVG content; renaming a non-SVG file to .svg.","solutions":["Open the imported .svg file in a text editor and confirm it has exactly one top-level <svg> element.","If the file is HTML or XML, re-export the asset from your design tool as a real SVG.","If the SVG is a fragment (no <svg> wrapper), wrap its contents in <svg xmlns=\"http://www.w3.org/2000/svg\">…</svg>.","Run the file through an SVG validator / `svgo --pretty` to surface structural problems."],"exampleFix":"// before — file contains: <html><body><svg>…</svg></body></html>\nimport logo from './logo.svg';\n\n// after — logo.svg is a single root element\n// <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 100 100\">…</svg>\nimport logo from './logo.svg';","handlingStrategy":"validation","validationCode":"import { readFileSync } from 'node:fs';\nfunction hasSvgRoot(path: string): boolean {\n  const txt = readFileSync(path, 'utf8').trim();\n  return /^<svg\\b/i.test(txt) && /<\\/svg>\\s*$/i.test(txt);\n}","typeGuard":"function isSvgFile(content: string): boolean {\n  return /<svg[\\s>][\\s\\S]*<\\/svg>/i.test(content);\n}","tryCatchPattern":null,"preventionTips":["Export SVGs from a design tool rather than hand-writing HTML-wrapped files.","Add a pre-commit lint with svgo --pretty to enforce a single <svg> root.","Do not rename non-SVG files to .svg."],"tags":["svg","assets","file-format","validation"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}