Skip to content
HomePagesHomePages template kit

template-invalid

"A template's template.ts is missing, unloadable, or violates the composition contract."

Venue: check — A template’s template.ts is missing, unloadable, or violates the composition contract.

A template folder (templates/<key>/) must have a template.ts at its root, and that file must:

  • exist — a template folder with none at all reports this once, naming the missing file;
  • load, and default-export a defineTemplate({ … }) result — the file is compiled and evaluated, so a syntax error, an unresolvable import, or a missing default export all report here;
  • declare a compositionkey, name, output, scope, and pages, built by composePages over page() entries whose sections come from composeSections; object key order is page order;
  • name itself in at most two words — the display name goes on one truncating line in the picker card, the editor chrome and the dashboard, so "Classic Carousel" passes and "Classic Listing Carousel" is refused; the description belongs to the preview blurb;
  • declare a valid site map — page ids and paths unique, every page labelled and carrying at least one section, exactly one page at "/" (never optional), and every other path one lowercase-kebab segment;
  • declare a valid canvas grouping, when it declares one — explicit canvases (built by composeCanvases over canvas() entries) must partition the pages: every page on exactly one canvas, no canvas empty, canvas ids unique, every canvas labelled; omit canvases for the class default;
  • agree with its own folder — the declared key must match the template folder name it lives in;
  • reference real sections — every composed entry must resolve to a section this workspace actually has;
  • satisfy the whole-composition nav contract — the cross-section rules that need the full, resolved section set rather than any one section in isolation.

Every violation collects into the same run; a composition with several wrong references reports all of them together.

A section’s own declarations describe that section in isolation — they say nothing about which page it appears on, or in what order. template.ts is where that composition is declared: the whole site map in one file, and the only place a downstream consumer (the publisher, the fill pipeline, the editor) can go to learn a template’s actual shape.

Most of that contract is enforced by the compiler: composeSections resolves every entry against the section’s real schema, so a renamed section stops compiling in your editor instead of failing a run later. This rule is the rest of it — what needs the whole composition at once.

Read the message: it names the template, the section instance, and what’s wrong. Missing entirely, add one. Otherwise, fix the field the message points at.

templates/acme-modern/
template.ts
sections/
hero/
templates/acme-modern/template.ts
import { composePages, composeSections, defineTemplate, page, presets } from "@homepages/template-kit";
import { schema as heroSchema } from "./sections/hero/schema";
export const sections = composeSections({
hero: { schema: heroSchema, instanceId: "hero-1", required: true },
});
export default defineTemplate({
key: "acme-classic",
name: "Acme Classic",
output: "website",
formats: [presets.website],
scope: "global",
pages: composePages({ home: page("Home", "/", [sections.hero]) }),
reconcile: [],
});
Template "acme-modern": declared key "acme-classic" does not match the template folder name "acme-modern".
templates/acme-modern/template.ts
import { composePages, composeSections, defineTemplate, page, presets } from "@homepages/template-kit";
export const sections = composeSections({
hero: { schema: heroSchema, instanceId: "hero-1", required: true },
});
export default defineTemplate({
key: "acme-modern",
name: "Acme Modern",
output: "website",
formats: [presets.website],
scope: "global",
pages: composePages({ home: page("Home", "/", [sections.hero]) }),
reconcile: [],
});
  • The schema system — what a template declares and the shape it is validated against.
  • schema-invalid — the same kind of cross-reference check, one level down, on a single section’s own contract files.
  • no-templates — reported instead when the workspace has no templates/<key>/ folder to check at all.