Errors & Warnings
Errors & Warnings Guide
See also: Errors & Warnings Catalog — a reference index of every error and warning message, organized by component.
Super-Simple Includes produces clear, actionable messages when something needs attention. This guide explains the messages you are most likely to encounter.
Exit Codes
For a complete list of exit codes and their meanings, see Exit Codes.
Understanding Messages
Errors ❌
Errors stop the build and must be fixed before proceeding:
- Missing or invalid configuration file
- Invalid TOML syntax
- Security violations (absolute paths, path traversal)
- Missing source directories or files
- Invalid include tokens
Warnings ⚠️
Warnings indicate potential problems but do not stop the build:
- Leftover include tokens (unresolved in output)
- Invalid filenames that may cause cross-platform issues
- Unused include sources
- HTML structure or accessibility issues (see below)
- Emoji tokens using text-default Unicode presentation (see below)
In development you can often proceed despite warnings. For CI/CD pipelines, use --strict to treat all warnings as errors.
HTML Structure & Accessibility Warnings
ssi validate checks every generated .html file for structure and accessibility issues. These are warnings by default; they become errors under --strict.
Missing lang attribute
⚠️ Missing lang attribute on <html> in 'index.html': add lang="en" (or the
appropriate language code) so screen readers pronounce content correctly.
Every full HTML page should have a lang attribute on the <html> element. Screen readers use it to select the correct voice and pronunciation rules. Without it, a reader configured for English may mispronounce content in other languages, and vice versa.
Fix: Add lang to your <html> tag:
<html lang="en">
Missing alt on <img>
⚠️ Missing alt attribute on <img> in 'index.html': add alt="" for decorative
images or descriptive alt text for meaningful images.
Every <img> element must have an alt attribute. Screen readers announce images to users who cannot see them; without alt, the image is invisible to assistive technology.
- For meaningful images, write a description:
alt="A diagram of the processing pipeline" - For decorative images (icons, spacers, purely visual flourishes), use an empty string:
alt="". An emptyalttells screen readers to skip the image entirely.
Fix:
<!-- Meaningful image -->
<img src="diagram.png" alt="Processing pipeline with three stages">
<!-- Decorative image — explicitly mark as decorative -->
<img src="divider.png" alt="">
Unlabeled form input
⚠️ Unlabeled form input in 'index.html': <input> element has no associated
label, aria-label, or title attribute.
Form inputs need a programmatic name so that screen readers can announce what the field is for. Visual labels placed nearby in HTML are not enough — the association must be explicit.
Valid ways to label an input:
<!-- Explicit label with for= -->
<label for="email">Email address</label>
<input type="email" id="email">
<!-- Wrapping label (implicit association) -->
<label>Email address <input type="email"></label>
<!-- aria-label when a visible label would be redundant -->
<input type="search" aria-label="Search the site">
<!-- title attribute -->
<input type="text" title="Your full name">
Submit, reset, hidden, and image inputs are exempt — they have accessible names from their value, content, or alt attributes.
Heading hierarchy skip
⚠️ Heading hierarchy skip in 'page.html': h3 follows h1 (levels must not skip)
Headings create a document outline. Skipping levels (e.g., jumping from <h1> to <h3> without an <h2> in between) breaks the outline structure that assistive technology and search engines rely on.
Fix: Use heading levels in order. Each level should be a sub-section of the one above it.
Multiple <h1> elements
⚠️ Multiple <h1> elements in 'page.html': pages should have exactly one <h1>
Each page should have exactly one <h1> that describes the page. Additional top-level headings should use <h2>.
Div-soup
⚠️ Div-soup detected in 'page.html': 8 <div> elements with no semantic
sectioning elements. Use <main>, <nav>, <article>, <section>, <header>,
<footer>, or <aside> instead.
Pages that use many <div> elements without any semantic HTML5 sectioning elements lose structural information. Assistive technology uses elements like <main>, <nav>, and <article> to help users navigate pages efficiently.
Fix: Replace generic <div> wrappers with appropriate semantic elements where the content warrants it.
Duplicate id attributes
⚠️ Duplicate id attribute in 'page.html': id='section' appears more than once.
Each id must be unique within a page.
The id attribute must be unique within a page. Duplicate IDs break CSS targeting, JavaScript, and fragment links.
Non-conforming elements
⚠️ Non-conforming HTML element in 'page.html': <center> is not valid HTML5 —
use CSS or a modern semantic equivalent.
Elements like <center>, <font>, <marquee>, and <blink> are not part of the HTML5 specification. Use CSS for presentation instead.
Pedantic-only checks (--pedantic)
These checks are off by default and require --pedantic:
- Missing
<h1>— pages that have headings but no<h1>; pages with no headings at all are not flagged - Inline
styleattributes — use external stylesheets linked with<link rel="stylesheet"> - Embedded
<style>tags — move styles to an external stylesheet
Invalid ARIA role
⚠️ Invalid ARIA role in 'page.html': role="buton" is not a valid ARIA role.
See https://www.w3.org/TR/wai-aria/#role_definitions for valid values.
Every role= attribute value must be a valid ARIA role name from the ARIA 1.2 specification. This check catches typos and invented names.
Fix: Correct the typo or use a valid role from the ARIA role taxonomy.
<!-- Invalid -->
<div role="buton">Click me</div>
<!-- Valid -->
<div role="button" tabindex="0">Click me</div>
ARIA reference to missing id
⚠️ ARIA reference to missing id in 'page.html': aria-labelledby="header-label"
references an id that does not exist in this page.
aria-labelledby and aria-describedby are space-separated lists of id values. Every referenced id must exist somewhere in the same page. A broken reference silently removes the accessible name or description.
Fix: Ensure the referenced id exists in the page, or correct the attribute value.
<!-- Broken — no element with id="lbl" exists -->
<input type="text" aria-labelledby="lbl">
<!-- Fixed -->
<span id="lbl">Your name</span>
<input type="text" aria-labelledby="lbl">
aria-hidden on interactive element
⚠️ aria-hidden on interactive element in 'page.html': aria-hidden="true" on a
focusable element removes it from the accessibility tree while keeping it
keyboard-reachable — use display:none or remove the element instead.
aria-hidden="true" removes an element from the accessibility tree — screen readers skip it. But unlike display:none, it does not remove the element from the tab order. A keyboard user can still tab to the element, but a screen reader user hears nothing when focus arrives there.
Affected interactive elements: <button>, <input> (non-hidden), <select>, <textarea>, <a href="...">, and any element with tabindex ≥ 0.
Fix: If the element should be hidden entirely, use display:none or hidden. If it should be visible but decorative, reconsider whether it needs to be interactive at all.
<!-- Wrong — keyboard-reachable but invisible to screen readers -->
<button aria-hidden="true">Cancel</button>
<!-- If truly hidden: use display:none -->
<button style="display:none">Cancel</button>
<!-- If interactive and visible, just remove aria-hidden -->
<button>Cancel</button>
What ssi validate does not check
ssi validate catches specific, unambiguous ARIA mistakes. It does not perform a full ARIA audit:
- Role–attribute compatibility — whether the ARIA attributes used are valid for the assigned role requires an ~80 × ~50 mapping table and is not implemented
- Implicit roles from native HTML — native elements have implicit ARIA roles (
<button>is implicitlyrole="button"), but the validator only inspects explicitrole=attributes aria-hiddeninheritance — only direct use ofaria-hidden="true"on an element is checked; if a parent carries it, focusable descendants are not flagged- Link text quality — generic link text like “click here” or “read more” is not detected
For a complete ARIA audit, use a dedicated tool such as axe, NVDA, or the browser’s built-in accessibility inspector alongside ssi validate.
Emoji Text-Default Presentation
When you run ssi validate, you may see a warning like this:
⚠ Emoji '☺' (U+263A) has text-default Unicode presentation — use '☺️' (with VS16 U+FE0F) for reliable emoji rendering
What this means: The emoji you chose as a token prefix is one of a group of older Unicode symbols that exist in two forms. The bare form (e.g., ☺) may show up as a plain monochrome text glyph in some terminals, log viewers, and email clients. The emoji form (e.g., ☺️) adds an invisible character — called VS16 or U+FE0F — that tells the rendering system to use the colorful emoji version.
Your site is fine. SSI processes both forms identically. This warning only matters if you care how token names look in log output and status messages.
To fix it: Copy the suggested form from the warning and update your ssi-config.toml:
# Before (may render as text glyph in some terminals)
[[step]]
emoji = "☺"
path = "blocks/"
processing = "include"
type = "html"
# After (VS16-qualified form, renders as emoji everywhere)
[[step]]
emoji = "☺️"
path = "blocks/"
processing = "include"
type = "html"
Or ignore the warning entirely — it has no effect on your output.
Which emoji are affected? Mostly older symbols from before emoji were common: arrows (↩, ↪), card suits (♥, ♠, ♣, ♦), miscellaneous symbols (☺, ✌, ⚠, ☎, ✂), and several dozen others. Modern emoji like 😀, 🚀, and 📄 are not affected.
Common Issues
“Leftover include token”
Your output contains unprocessed tokens like the 📎 header token.
Fix:
- Check that the emoji in the token matches the emoji in
ssi-config.tomlexactly - Verify the file exists:
blocks/header.html - Check for typos in the token name
“Invalid filename”
A file contains spaces or special characters.
Fix:
# See the suggested replacement in the warning
mv "my file.txt" "my-file.txt"
“Permission denied”
Cannot read source files or write output.
Fix:
# Check permissions
ls -la site/
chmod -R u+r site/ # Add read permission
chmod u+w output/ # Add write permission
Strict Mode
Use --strict to treat all warnings as errors. Any warning produces exit code 53 and stops the build. This is useful for CI/CD pipelines where warnings should be resolved before merging or deploying.
ssi validate --strict my-site/
ssi deploy --strict my-site/ output/
Debug Tips
Enable verbose output
Use --verbose to see detailed processing information:
ssi validate --verbose my-site/
This shows configuration loading details, file processing steps, and include resolution.
Start simple
- Test with one HTML page and one include source
- Add complexity gradually
- Isolate issues by removing components one at a time
Check your configuration
A minimal working configuration looks like this:
[[step]]
emoji = "📄"
path = "pages/"
processing = "page"
[[step]]
emoji = "📎"
path = "blocks/"
processing = "include"
type = "html"
More Help
- Full validate command reference — all options and exit codes
- Configuration reference —
ssi-config.tomlsyntax - Working examples — complete example sites