A hook that fails
A hook is a program you wired in. When one stops a build the message names it, and says where the fault is: in a block you wrote, in the hook, or in how it was wired.
What it looks like
ERROR: hook 'diagrams' did not answer 'describe' (died, refused, or past the 30s
timeout)
│ connect: No such file or directory
See https://keystone.knight-owl.dev/errors/hook-failed/#it-did-not-answer
ERROR: hook 'diagrams' on a 'mermaid' block starting "flowchart LR A --> B":
syntax error on line 3: expected an arrow
ERROR: hook 'diagrams' on a 'mermaid' block starting "flowchart LR A --> B"
returned no body
See https://keystone.knight-owl.dev/errors/hook-failed/#its-reply-was-unusable
WARN: hook 'diagrams': no serif font is installed
Diagrams letter in the fallback font.
ERROR: hook 'diagrams': DIAGRAM_THEME is 'midnight', which is not a theme this
renders
Valid: dark, light, neutral
Set it to one of those, or leave it empty.
ERROR: hook 'diagrams' sent a diagnostic Keystone cannot read
│ {"message":"the renderer is not installed","severity":"error"}
A diagnostic names the pieces of a message; Keystone assembles them.
Name the pieces of your message, with a severity of 'warning' or 'error'
beside them.
See https://keystone.knight-owl.dev/hooks/writing-a-hook/#writing-the-message
What it means
It did not answer
Something is listening and did not reply — a hook that crashed after binding its socket, or one still working past the timeout.
A socket with nothing behind it is the first of those. The file outlives the process that bound it for as long as some container holds the volume, so a hook that died inside a container still running looks like one that is there.
A renderer that never started at all produces no message: with no socket to find, Keystone runs as though no hook were wired, and every claimed fence comes out as a code listing. See Nothing was rendered below.
Clear the socket, and check that the build waits
make down clears one: the volume it lives in empties with the containers,
and the cache stays where it is. A rebuild alone will not, while whatever
holds the socket is still up. For a renderer that is only slow, raise
KEYSTONE_HOOKS_TIMEOUT in
project.conf. For one that is not
ready in time, see Wiring one in.
It disagrees with the engine
speaks …; this engine speaks … means the hook talks a protocol version this
Keystone does not. needs Keystone … means the hook set a floor your engine is
below. claims no languages means it answered without saying what it renders.
Match the two versions
Upgrade Keystone to the floor the hook asks for, or run a
hook release built for the engine you have. claims no languages is the
hook's own defect.
It cannot run as configured
hook 'name': followed by the hook's own words is the hook reporting on the
settings it was given, before any block was read — a value it does not accept,
or something it needs and has not got. The colon is the tell: every message
Keystone writes about a hook reads hook 'name' sent …, claims …,
speaks ….
What it names is in your project's configuration, not your manuscript. Everything one hook has to say is reported together, so a misconfigured project is not fixed one build at a time.
A WARN: line from a hook is the same report at a lower severity: worth
knowing, and the build goes on — unless
strict builds are on, which
promote it like any other warning.
Fix the setting it names
The hook quotes the value it cannot use. Your settings for it are in
project.conf.
It refused the block
A message naming a block, then the hook's own account of it, is the hook saying
it could not render that block and why — a syntax error, a directive it does not
know. The block is named by its opening, so one fence of twenty is identifiable.
Lines behind a │ bar came from
somewhere else again — the renderer the hook drives, or the socket it answers
on.
Fix the block it names
This one is yours: the message carries the block's opening and the hook's own reason. Nothing in the wiring is at fault.
Its reply was unusable
Keystone refuses a reply rather than build a book around half of it.
From a transform: returned no body, returned assets that are not a list,
returned two assets named …, returned asset N with no …, a name that is not
a filename, or an asset sent with no decodable bytes.
From a describe: an identity that is not a non-empty string, diagnostics
that are not a list, or a format group that names a format twice or puts
one in two format groups.
A diagnostic Keystone cannot read is either message in a shape it does not
know: a key that is not one of the pieces a diagnostic is made of, a value of
the wrong kind, nothing to report, or a severity that is neither warning nor
error. The object is quoted under the refusal, so the hook's words survive the
shape they arrived in.
This one is the hook's to fix
Writing a hook sets out what a reply must carry.
The cache rather than the hook
Your compose file mounts the cache for the engine, so a path message means you
pointed KEYSTONE_HOOKS_CACHE somewhere else.
which could not be createdandwhich could not be written— a path the container cannot make, or can reach and not write. Either needs a mount.could not write the hook cache at …— a directory that exists and cannot be written: the wrong owner, a:romount, or a compose file whose cache volume is gone.must be a whole number of megabytesandmust be a whole number of seconds—KEYSTONE_HOOKS_CACHE_MAXandKEYSTONE_HOOKS_TIMEOUTmean what they say.
Unset it, or mount the path you chose
Unset, the cache is the volume your compose file already declares, which
needs nothing of yours — restoring
.docker/docker-compose.yaml from git, or from a fresh copy of your
template, puts that volume back. That file is
the engine's to own,
so nothing of yours goes with it. A path of your own needs a mount you add.
Nothing was rendered
No error, a book that built, and every claimed fence in it rendered as a code listing. Keystone looks for a hook's socket once, before the build starts, and finding none it builds exactly as a template with no hook wired would. Nothing failed, so nothing is reported.
The build did not wait for the hook. A template that ships one — as
core-diagrams does — carries depends_on with condition: service_healthy,
and a hook that never becomes healthy stops the build with a message naming the
container. A hook wired in without that wait is the case with no message: the
engine starts first, finds no socket, and carries on.
Make the build wait for the hook
Carry depends_on with condition: service_healthy — see
Wiring one in.
Related
- Hooks — what a hook is and how one is wired in.
- Mistakes Keystone can't catch — a hook that answers with something plausible but wrong is not something Keystone can detect.