sveltejs/kit · error · Error
${relative} is missing ${tag}
Error message
${relative} is missing ${tag} What it means
The app template must contain the placeholders %sveltekit.head% and %sveltekit.body% so SvelteKit can inject the page head and rendered body. load_template reads the template and throws if either placeholder is missing, because rendering would silently produce broken pages otherwise.
Source
Thrown at packages/kit/src/core/config/index.js:89
* required content.
* @param {string} cwd
* @param {ValidatedConfig} config
*/
export function load_template(cwd, config) {
const { files } = config;
const relative = path.relative(cwd, files.appTemplate);
if (!fs.existsSync(files.appTemplate)) {
throw new Error(`${relative} does not exist`);
}
const contents = fs.readFileSync(files.appTemplate, 'utf8');
const expected_tags = ['%sveltekit.head%', '%sveltekit.body%'];
expected_tags.forEach((tag) => {
if (contents.indexOf(tag) === -1) {
throw new Error(`${relative} is missing ${tag}`);
}
});
return contents;
}
/**
* Loads the error page (src/error.html by default) if it exists.
* Falls back to a generic error page content.
* @param {ValidatedConfig} config
*/
export function load_error_page(config) {
let { errorTemplate } = config.files;
// Don't do this inside resolving the config, because that would mean
// adding/removing error.html isn't detected and would require a restart.
if (!fs.existsSync(config.files.errorTemplate)) {
errorTemplate = url.fileURLToPath(new URL('./default-error.html', import.meta.url));View on GitHub (pinned to 03f1687fe6)
Solutions
- Add the missing placeholder(s) into the template: `%sveltekit.head%` inside <head> and `%sveltekit.body%` inside <body> (wrapped in a div or body content).
- Restore the default src/app.html from the SvelteKit template (`npx sv create` output or the docs) if unsure of the shape.
- Check for preprocessing/formatting tools that might strip the placeholder comments/tokens and exclude app.html from them.
Example fix
<!-- before: src/app.html missing placeholders --> <html><head></head><body><div id="app"></div></body></html> <!-- after --> <html><head>%sveltekit.head%</head><body data-sveltekit-preload-data="hover"><div style="display: contents">%sveltekit.body%</div></body></html>
Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs';
const contents = fs.readFileSync('src/app.html', 'utf8');
for (const tag of ['%sveltekit.head%', '%sveltekit.body%']) {
if (!contents.includes(tag)) throw new Error(`src/app.html is missing ${tag}`);
} Try / catch
try {
await viteBuild();
} catch (e) {
if (e.message.includes('is missing %sveltekit.')) {
console.error('Add the missing %sveltekit.head% / %sveltekit.body% placeholder to app.html');
}
throw e;
} Prevention
- Keep the standard placeholders in app.html whenever editing it
- Start edits from the official default template
- Exclude app.html from minifiers/transformers that might strip tokens
- Add a smoke test asserting both placeholders exist in the template
When it happens
Trigger: write_server -> load_template reads files.appTemplate successfully, but its contents do not include `%sveltekit.head%` or `%sveltekit.body%` (indexOf returns -1 for either tag).
Common situations: Users copy a plain HTML file as app.html without the placeholders; manually stripping tags when 'cleaning up' the file; using a template written for an older framework version; tooling that minifies/rewrites the file and removes the tokens.
Related errors
- The _headers file should be placed in the project root rathe
- The _redirects file should be placed in the project root rat
- ${relative} does not exist
- No matcher found for parameter '${name}'${file ? ` in ${file
- routes.include must contain 100 or fewer routes
AI-assisted analysis of sveltejs/kit@03f1687fe6 (2026-09-02).
Data as JSON: /api/errors/4a030f9ec67c941f.
Report an issue: GitHub.