Skip to content

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 element — instead of silently letting it through.

This page is the map. Hit a message on the terminal? Find its row below and follow it to a page with the exact message and how to fix it.

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 artifact.

Most diagnostics are Keystone's own, prefixed WARN: or ERROR:. A few come from the tools underneath — Pandoc reading your Markdown, fetching an image, or resolving a citation — and print in Pandoc's own [WARNING] … format instead (their rows are marked (Pandoc) below). They're warnings all the same; strict mode fails on them too.

Where checks happen

Your book passes through stages — reading your Markdown, expanding shortcuts, running the handlers, composing the running heads and feet, converting math, and resolving citations, images, and configuration — and each can fail in its own way. Find the row whose message matches yours:

Build stage What can go wrong A message you'll see Severity
Reading your Markdown Unclosed div Div at … unclosed … WARN
Reading your Markdown A malformed fence or unclosed span (nothing — silent)
Expanding shortcuts Unrecognized class Unrecognized class '…' WARN
Expanding shortcuts Unrecognized attribute ignores unrecognized attributes WARN
Expanding shortcuts Missing required field Shortcut '…' requires … ERROR
Running a handler Invalid attribute value unrecognized size · unknown type · invalid cols WARN
Running a handler Undeclared mark mark '…' is not declared ERROR
Running heads & feet Unknown placeholder unrecognized placeholder '{…}' WARN
Running heads & feet Header/footer text suppressed …-text suppressed WARN
Converting math Math that can't convert equation(s) cannot be converted ERROR
Resolving citations (Pandoc) Undefined citation Citeproc: citation … not found WARN
Fetching images (Pandoc) Image not found Could not fetch resource … WARN
Resolving configuration Missing or invalid metadata Missing required metadata fields · draft-scale must be… ERROR

The message column is a representative fragment, not the exact text. Handler warnings in particular share the shape <handler>: <problem> '<value>' — so font: unknown family 'garmond' is the Running a handler row even though its words aren't listed. Match on the stage and the shape, not a literal string.

Finding the offender

A compiler usually points at a file and line number. Keystone can't — not yet. It builds on Pandoc, whose Markdown reader doesn't record where each element came from in your source, so there is no line for Keystone to report. Keystone will surface real line numbers as soon as Pandoc can supply them for Markdown.

Until then, every message names the offending element with a compact selector you can search for — its classes, its identifier, and a snippet of its leading text:

WARN: aside: unknown type 'todo' (in .aside "A callout whose type is not one of the d…")

That (in …) is your search key. Search your source for the quoted class, the identifier, or the leading-text snippet — in the manuscript for a content mistake, or in shortcuts.yaml and your metadata for a configuration one.

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:
  vspace: unrecognized size 'banana'
  No new artifact was written. Fix the warnings above, or unset KEYSTONE_WARNINGS_AS_ERRORS for a lenient build

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 — nothing half-built ever lands in artifacts/. Where it can, it collects a run's warnings and reports them together rather than stopping at the first, so you fix them in one pass. 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 source is a quote without an attribution line — a normal choice, not an error.
  • Undefined conditional symbols. ifdef/ifndef gate 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; they matter as much as the checks themselves.

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.