Troubleshooting
Start with the symptom. The diagnostic codes at the end cover every error and warning the CLI prints.
An icon is blank in Safari#
Chromium and Firefox show it, WebKit does not. Three causes, in order of likelihood:
- The sprite is on another origin. WebKit (and every browser, in fact) refuses an external
<use>from a CDN or another subdomain.publicPathmust be a same-origin path such as/icons. That is why the config rejectshttps://…and//…values. Serve the sprite from the app’s own origin. Cross-origin sprites are planned as an opt-in (TSK-067). - You built the
<use>yourself from the icon name. Icons that use gradients, masks, clip paths, filters or markers do not render through an external<use>in WebKit. So svgworm delivers them from the stack file as<img>or a CSS mask (how it works). Always render throughIcon, which readsdeliveryfrom the manifest. Or readicons[name].deliveryyourself. - The icon has a
<style>that could not be inlined. The build would have failed withstyle/leftoverorstyle/unscopable. If an old sprite is still deployed, rebuild and redeploy.
Still blank everywhere? Open the sprite URL from the manifest in a tab. A 404 means the sprite is not where the page expects it.
The sprite 404s#
Every icon is missing, and the network tab shows a 404 for /icons/icons.<hash>.svg.
-
A mount point is missing from
publicPath. NextbasePath, Astrobaseand Vitebaseare not added to the URLs svgworm writes. With a base of/docs, set--public-path /docs/iconsor"publicPath": "/docs/icons"insvgworm.config.json, then rebuild (Next.js, Astro, Vite). -
outDiris inside a source folder. For example--source public/iconswith the defaultoutDirpublic/icons: the next build would read the sprites as icons, so the config is refused. Keep the SVG files outsidepublic/(icons/is the default) and let svgworm write topublic/icons, or pick another output with--out-dir. -
outDirandpublicPathdisagree. A customoutDirmust be served atpublicPath. Underpublic/svgworm derives it (public/spritesgives/sprites), so passpublicPathonly to add a mount point. For anoutDiroutsidepublic/, pass both flags together: the config refusesoutDiron its own. -
The deploy replaced the sprite under cached HTML. Each build writes a new hashed file and removes the old one. HTML from the previous deploy that a CDN still serves points at a hash that is gone. A tab that already loaded the sprite is fine, because every icon is in that one file.
svgworm keeps only the current sprite on purpose, so the output stays a pure function of your icons (ADR-0022). Fix it in the deploy:
- Upload the sprite before the HTML.
- Keep HTML caches short, or purge them on deploy.
- When you sync
outDirto a bucket, do not delete old files:aws s3 syncwithout--delete,rclone copyinstead ofrclone sync.
Hashed names never collide, so old files can expire by age. The sprite itself can be cached for a year, because its name changes with its content.
-
The sprites are on a CDN (
crossOrigin: "inject") and everyuseicon is blank. Check the console forsvgworm: sprite not loaded from …. It has three causes:- A missing
Access-Control-Allow-Originon the CDN. - A CSP without the CDN in
connect-src. - A sprite that no longer matches its integrity. Upload
outDiragain.
No message at all means the loader tag is missing. Render
<SpriteLoader />or<SvgwormLoader />once (Sprites on a CDN). - A missing
-
The committed output is stale. Someone changed an icon and did not run
svgworm, so the manifest in git points at a sprite that no build produced.svgworm checkin CI catches this before a deploy.
An icon is black, or the wrong color#
- It is multicolor and you expected it to follow the text color. svgworm keeps the colors of an icon that has more than one (a two-tone icon, a dark outline with a white fill). Add
{ "match": "<name>", "color": "mono" }tocolorin the config (color overrides) only if the icon really is one color. Otherwise the build fails withcolor/forced-mono-unsafeand says what it found. - It is mono and black. It follows
currentColor, so it is black because the parent’scoloris black. Setcoloron the parent or on the icon (coloring). fillon the<svg>does nothing. The symbol’s ownfill="currentColor"wins inside a<use>.coloris the knob, notfillorstroke.- It is an
imgicon andcolordoes nothing. Correct: an<img>cannot inheritcurrentColor. The icon is multicolor and needs the stack file. It keeps its own colors by design. - A mono icon lost its colors. A
monorule matched it, so svgworm flattened every paint tocurrentColor. Narrow the rule’smatchglob, or add a laterpreserverule for that icon. The last match wins.
Name collision#
error [name/collision] icons/arrow-left.svg: name "arrow-left" is produced by 2 files: icons/arrow-left.svg, icons/arrow_left.svg. Fix: rename one of the files
Names come from file paths, lower-cased, with every run of _, spaces and punctuation turned into one - (naming). Two files that normalize to the same name are an error, never a silent rename. Examples:
arrow_left.svgandarrow-left.svg.Icon.svgandicon.svg.home.svgin two folders whenflattenis on.
The message says which fix applies:
- Rename one file.
- Turn
flattenoff, so folder names stay in the icon names (ui/homeandnav/home). - Leave one out: when one of the files comes from an icon package, leave it out with
excludeinstead.
There is no alias map: a name always comes from its path (ADR-0025).
svgworm check fails in CI#
check compiles the icons and compares the result with the files in git. Exit 1 lists what differs.
missingorchangedfiles, iconsaddedorremoved: someone changed an icon and did not commit the output. Runsvgwormlocally and commitpublic/icons/andsvgworm/.- Everything
changedand the icons match: local and CI run different svgworm versions, and the output changed between them (a minor can change the bytes before 1.0). Commit the lockfile and install from it in CI. - Only
.js,.d.tsor.jsonfileschangedon Windows runners: git converted line endings. svgworm writes LF. Keep the generated files LF with a.gitattributesrule such assvgworm/** text eol=lfandpublic/icons/** text eol=lf. stalesprite: an old hashed file is still inpublic/icons/. Runsvgwormand commit the deletion.- It passes locally and fails in CI with
build/no-icons: the source folder is not in the checkout. Checksourcesand.gitignore. - Run
checkbefore the build step, not after it.prebuildregenerates the files, so a check after the build proves nothing.
Diagnostic codes#
The CLI prints each one as [code] file: message on stderr. --json lists them in errors or warnings. Errors stop the build and nothing is written. Warnings do not.
| Code | Kind | Meaning and fix |
|---|---|---|
name/collision |
error | Two files map to one name. Rename one, or turn flatten off (above) |
name/case-folded |
warning | A file or folder name is camelCase or PascalCase (ArrowLeft.svg), so the name folds case (arrowleft). The message suggests the name for a readable one (arrow-left.svg); rename it to silence the warning. A camelCase folder is reported once, not once per file |
name/empty-segment |
error | A file or folder name has no letters or digits left after normalizing (🔥.svg, ---.svg, a folder named _). Rename it |
path/invalid-encoding |
error | The file name is not valid Unicode (undecodable bytes, lone surrogates). Rename the file |
path/not-svg |
error | The file does not end in .svg. Only .svg files are inputs |
svg/invalid-encoding |
error | The file is not valid UTF-8. Re-export it as UTF-8 |
svg/invalid-xml |
error | The file is not well-formed XML; the message gives the line and column. Re-export it, or fix the markup |
svg/not-svg-root |
error | The root element is not <svg> (an HTML page saved as .svg, an empty file). Replace the file |
svg/forbidden-element |
error | <script>, <foreignObject> and similar are never put in a sprite. Remove them |
svg/forbidden-attribute |
error | on* event handlers and javascript: values are rejected. Remove them |
svg/external-reference |
error | href or url(…) points outside the file (another file, a URL). Inline the asset or remove the reference |
style/unscopable |
error | A <style> has an at-rule (@media, @keyframes) that cannot be inlined. Convert it to attributes, or drop it |
style/leftover |
error | A <style> has rules that cannot be inlined, such as :hover on an element that exists. The message lists them. Rules that match nothing are dropped, so unused Illustrator classes are fine |
viewbox/underivable |
error | No viewBox, and width/height are not plain numbers or px. Add a viewBox |
viewbox/invalid |
error | The viewBox is not four numbers with a positive width and height. Fix it |
viewbox/derived |
warning | The viewBox was derived from width and height. Fine; add a viewBox to the file to silence it |
color/forced-mono-unsafe |
error | A mono rule matched an icon with content that cannot follow currentColor (a raster image, a pattern fill, a CSS variable paint, a color animation). Use preserve, or narrow the rule |
io/unreadable |
error | A source file cannot be read. Check its permissions |
build/no-icons |
error | No .svg file in the source folders. Check sources (exit code 2) |
cli/usage |
fatal | A usage or config error: unknown flag, missing config, an invalid key, a bad publicPath. The message says which (exit code 2) |
cli/unexpected |
fatal | An I/O failure or a bug in svgworm, reported as unexpected failure: … (exit code 1). Please open an issue with the message |
The same table, with the agent-oriented wording, is in the Next.js and Astro agent skills.