How Keystone checks your book
Keystone is a compiler for books, and like any compiler it checks your source as it builds. When something is wrong it tells you — on the terminal, naming the offending element, metadata key, or file and line — instead of silently letting it through.
This page is the map. Most messages on the terminal end with a See link to the page that covers them. One without a
link is found below by the stage that raised it; Pandoc's own warnings carry none, so their entries quote a fragment of
the line.
Two outcomes: warnings and errors
Every Keystone diagnostic is one of two severities.
WARN:— something is off, but the build has a safe fallback. The element is dropped or its bad input ignored, and you still get an artifact — unless you've enabled strict mode, where any warning fails the build.ERROR:— the build cannot proceed correctly, so it stops and writes no completed artifact.
A lint finding opens with its location, manuscript/chapter.md:4:1: ERROR:, which your editor can open.
A line behind a │ bar is quoted rather than composed, and printed as given, so any line numbers or carets it carries
still point where they should: a tool's output — Pandoc's math converter, the Lua loader, your operating system — or
your own markup:
ERROR: 1 equation(s) cannot be converted for epub output
│ Could not convert TeX math \begin{array}[b]{r} a \\ b \end{array}, rendering as TeX:
│ \begin{array}[b]{r} a \\ b \end{array}
│ ^
│ unexpected "["
Some checks are Pandoc's rather than Keystone's — reading your Markdown, fetching an image, resolving a citation. Their
warnings carry the same WARN: prefix as any other, so there is one shape to recognize. The entries below are marked
(Pandoc) to tell you which tool to consult if you need to dig, and strict mode fails on them too. When Pandoc stops
the build outright, the progress report marks the stage ✗, and Keystone closes with an ERROR: line naming
the pass that stopped, Pandoc's own words quoted beneath it.
Where checks happen
Your book passes through stages, and each can fail in its own way. They run in the order below — the same order and the
same names a build reports as it works (see Watching a build run). Starting
the build falls before any stage runs, where a build has none to name yet. Anywhere holds what can stop any stage,
and Importing a document is make import's, apart from any build.
Starting the build
ERRORA format Keystone doesn't writeERRORA setting value it can't readERRORNopandoc.yamlto readERRORA YAML file it can't readERRORA target name that doesn't matchERRORA code theme the image doesn't carryERROR·WARNA bibliography or style that isn't thereERROR·WARNA build configuration it can't useERRORA chapter that isn't thereWARNThe project name is still the default
Compiling your shortcuts
ERROR·WARNA mistake in a shortcut definition
Linting your manuscript
Reading your Markdown
ERRORA chapter Pandoc can't read (Pandoc)WARNUnclosed div (Pandoc) —Div at … unclosed …- silent A two-colon fence or unclosed span
Silent: nothing reports it, so check the book
Pandoc reads the markup as plain text and builds no element for a check to inspect, so the build succeeds and the stray characters print in your book.
Rendering hooks
ERRORA hook that fails
Expanding shortcuts
WARNUnrecognized classWARNUnrecognized attributeERROR·WARNInvalid attribute valueERROR·WARNUndeclared markERRORAn attribute a shortcut fixesERRORMissing required fieldERRORA shortcut used in a form it lacksERRORContent a shortcut doesn't takeERRORAn element naming two shortcutsERRORA class starting withks-
Saving hook assets
EPUB only, with a hook wired. What your hooks returned is saved for the pass that builds the book; nothing here stops a build.
Finding your fonts
EPUB only. The fonts your content names are noted for the archive to embed. A strict build's ✗ lands here for
a warning Pandoc raised anywhere in the pass that reads your chapters.
Collecting your fonts
EPUB only. The font files your book uses are gathered for the archive to embed; nothing here stops a build.
Resolving configuration
ERRORMissing or invalid metadataERROROverriding a reserved keyERRORUnknown page sizeERRORPage sized twiceERRORGeometry auto combinedERRORClass option belongs to another keyWARNUnsupported font sizeWARNUnrecognized font familyWARNAn unlisted heading without.unnumberedWARNMissing font licenseERRORDuplicate font fileWARNA problem in a custom font entry
Running handlers
What no shortcut attribute carries is checked here: an image's own width, a drop cap's opening paragraph, the heading
the appendices start at. A message linking no page names its handler, as in dropcap:; see the shortcut's entry in the
reference.
Composing headers & footers
Converting math
ERRORMath that can't convert
Embedding PDF metadata
Resolving citations
WARNUndefined citation (Pandoc) —Citeproc: citation … not found
Writing your artifact
WARNImage not found (Pandoc) —Could not fetch resource …
Promoting the artifact
Anywhere
Importing a document
The conversion reports as the one stage Converting to Markdown.
ERRORAn import that fails
Finding the offender
A compiler points at a file and line, and so does Keystone's lint, which reads your source before Pandoc does. The checks after it can't: they run on the document Pandoc builds, and Pandoc's Markdown reader doesn't record where each element came from in your source.
Instead, a diagnostic about a div, span or heading quotes its markup: the fence, span or heading, over the text it opens with. The quote is rebuilt from what Pandoc read: a heading carries the identifier Pandoc gave it, values are quoted, and punctuation is typeset.
WARN: shortcut 'aside' rejects type 'todo'
│ ::: {.aside type="todo"}
│ A callout whose type is not one of the d…
It takes one of the values listed.
The default 'note' stands in.
Valid: example, note, tip, warning
See https://keystone.knight-owl.dev/errors/invalid-value/
Search your source for a class or a value from it: the manuscript, or shortcuts.yaml for markup a shortcut's template
holds. A shortcut you used is quoted as you wrote it, even when a shortcut inside its template raised the warning. When
the value the message names isn't in the quote, it's in that shortcut's definition.
A metadata check is the exception: it has no element to point at, so it names the offending pandoc.yaml key in the
message itself (fontsize '13pt' is not a size this set allows) — search for the key.
Strict mode
While you're drafting, a warning shouldn't stop you — you want to see the page even with a placeholder that isn't quite right. So by default warnings are lenient: they print and the build continues.
Before you publish, flip that around. Set KEYSTONE_WARNINGS_AS_ERRORS=true in
project.conf and every warning becomes fatal:
ERROR: warnings-as-errors is enabled and the build produced warnings:
shortcut 'multicol' rejects cols '9'
No new artifact was written.
Fix the warnings above, or unset KEYSTONE_WARNINGS_AS_ERRORS for a lenient
build.
See https://keystone.knight-owl.dev/errors/#strict-mode
For a one-off check without editing config, make publish strict=true builds once in strict mode — see
Publishing your book.
Strict mode fails the build on any warning and promotes no artifact. Where the warning came from decides how you hear about it. The filters collect theirs and report them together after the build, as above, so you fix them in one pass. A warning about your configuration is raised before any filter runs, and stops the build where it is raised, reporting only itself:
WARN: the project name is still the default "keystone"
Set KEYSTONE_PROJECT, in project.conf or your environment, to name your own
output.
See https://keystone.knight-owl.dev/errors/default-project-name/
ERROR: warnings-as-errors is enabled
The warning above stops the build.
No new artifact was written.
Fix the warning above, or unset KEYSTONE_WARNINGS_AS_ERRORS for a lenient
build.
See https://keystone.knight-owl.dev/errors/#strict-mode
The lint's warnings and Pandoc's own turn fatal too, and there the build stops on a ✗, with the tool's reason quoted,
rather than with a warnings-as-errors message. Draft lenient, publish strict.
Silent by design
Not everything absent is a mistake. A few things are meant to be optional, so their absence is never reported:
- Optional attribution. An epigraph or pull-quote with no
sourceis a quote without an attribution line — a normal choice, not an error. - Undefined conditional symbols.
ifdef/ifndefgate on whether a symbol is defined; an undefined symbol is simply false, so the content is omitted. That's the whole mechanism, not a failure. See Conditional content.
Separately, there is a small set of mistakes Keystone cannot see at all — malformed markup that never becomes an element. Know the limits — the mistakes Keystone can't catch.
Under the hood
How the checks are implemented — the closed vocabulary, the shared diagnostic path, and how a core template forker
adds their own — is in Validation & diagnostics.