withastro/astro · error · Error
SVG file does not contain an <svg> element
Error message
SVG file does not contain an <svg> element
What it means
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.
Source
Thrown at packages/astro/src/assets/svg/utils.ts:36
if (svgOptimizer) {
try {
processedContents = await svgOptimizer.optimize(contents);
} catch (cause) {
throw new AstroError(
{
...AstroErrorData.CannotOptimizeSvg,
message: AstroErrorData.CannotOptimizeSvg.message(path, svgOptimizer.name),
},
{ cause },
);
}
}
const root = parse(processedContents);
const svgNode = root.children.find(
({ name, type }: { name: string; type: number }) => type === ELEMENT_NODE && name === 'svg',
);
if (!svgNode) {
throw new Error('SVG file does not contain an <svg> element');
}
const { attributes, children } = svgNode;
const body = renderSync({ ...root, children });
// Collect text content of <style> elements for head propagation and CSP hashing
const styles: string[] = [];
for (const child of children) {
if (child.type === ELEMENT_NODE && child.name === 'style') {
const textContent = child.children
?.filter((c: { type: number }) => c.type === TEXT_NODE)
.map((c: { value: string }) => c.value)
.join('');
if (textContent) {
styles.push(textContent);
}
}
}
View on GitHub (pinned to d081033d5f)
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.
Example fix
// before — file contains: <html><body><svg>…</svg></body></html> import logo from './logo.svg'; // after — logo.svg is a single root element // <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">…</svg> import logo from './logo.svg';
Defensive patterns
Strategy: validation
Validate before calling
import { readFileSync } from 'node:fs';
function hasSvgRoot(path: string): boolean {
const txt = readFileSync(path, 'utf8').trim();
return /^<svg\b/i.test(txt) && /<\/svg>\s*$/i.test(txt);
} Type guard
function isSvgFile(content: string): boolean {
return /<svg[\s>][\s\S]*<\/svg>/i.test(content);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- UnsupportedImageConversion
- CannotOptimizeSvg
- NoImageMetadata
- FailedToFetchRemoteImageDimensions
- ImageNotFound
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/610d7adffc00a047.
Report an issue: GitHub.