13 Troubleshooting
How to decode a diagnostic code, and fixes for the common failures: no kernel, a hanging cell, stale outputs, a port in use.
Reference: Cheat sheet · CLI · Configuration · Cell options · Troubleshooting · Accessibility · Licensing
The preview surfaces most problems as a small diagnostics note in the corner (non-fatal) or a dismissible error overlay (a failed render). Here are the common ones.
13.1 Reading a diagnostic
Every diagnostic names its file, its line, its severity and, where there is a mechanical fix, the fix itself, inline:
post.tmd:3: warning: unknown front-matter key `titel` (did you mean `title`?)
post.tmd:18: error: broken cross-reference: @fig-los (did you mean `@fig-loss`?)
A near-miss gets a did-you-mean, which your editor offers as a quick fix over
taliesin lsp. A name with no near neighbour gets none: Taliesin answers for its own
vocabulary and does not guess at another tool’s, so a key it has never had is reported as
unknown and left for you to look up.
13.2 Code cells show as source / “kernel unavailable”
Run taliesin doctor first: it names which
Python was resolved, which of the five sources chose it, and whether ipykernel imports.
The usual answer is that there is no Jupyter kernel. Set TALIESIN_PYTHON to a Python
that has ipykernel:
The diagnostics note usually quotes the kernel’s own error (e.g. “No module named ipykernel”), so fix that and save: the server respawns the kernel and re-runs automatically, no restart needed.
13.3 A cell hangs / runs forever
A cell is capped on silence, not on how long it runs. A cell that produces no
output for TALIESIN_CELL_SILENCE seconds (default 600) is interrupted
(SIGINT → KeyboardInterrupt); the warm kernel and prior cells survive. A long job
that prints progress as it goes resets that budget on every line, so it is never
interrupted however long it takes.
That means a genuinely long cell needs no configuration: print something
occasionally and it will run to completion. If a cell is legitimately silent for
longer than ten minutes, raise TALIESIN_CELL_SILENCE or set it to 0.
To bound a cell’s total runtime regardless of output, set the optional
TALIESIN_CELL_TIMEOUT (wall-clock seconds, off by default). The dev menu’s
Restart kernel drops and respawns the kernel if it’s truly wedged.
A cell can also refuse the interrupt, by installing its own SIGINT handler or by sitting inside a C extension that never checks for signals. The page then says “cell ignored the interrupt and is still running in the kernel”: the cell is occupying the warm kernel, every later cell queues behind it, and Restart kernel (or killing the interpreter, whose pid the server prints to the console) is what reclaims it.
13.4 A cell ran, but I think the kernel crashed
If a kernel process dies mid-session (a segfault in a native library, an out-of-memory kill), Taliesin self-heals rather than hanging. The cell that was running when the kernel died gets a “kernel exited before this cell ran; it will re-run on the next save” placeholder, and the next run notices the dead process, drops it, respawns a fresh kernel, and re-runs every cell from the top (the new kernel holds no state, so the warm prefix is gone). You don’t have to touch Restart kernel for this: a crash recovers on its own with the next save.
A failed start (a missing or misconfigured interpreter, not a mid-run crash) is
different: Taliesin backs off for about 20 seconds before retrying, so a genuinely
broken TALIESIN_PYTHON doesn’t re-hang every save. During the
backoff the cells render as source with the “kernel unavailable” diagnostic. Fix
the interpreter and the next save within a few tries recovers on its own; or hit
Restart kernel to clear the backoff and try immediately.
13.5 Outputs look stale, or I want to force a clean re-run
Cell outputs are cached in _freeze/ (gitignored), and the cache is correct by
construction: errors and #| cache: false cells are never persisted, and an edit to
your code always busts the key, so there are no stale hits to clear by hand. Two escape
hatches exist if you suspect it anyway:
The dev menu’s Restart kernel forces the next run to ignore disk-cache hits and re-execute every cell against a fresh kernel (then re-persist), so it doubles as a “re-run everything from scratch” button.
TALIESIN_NO_CACHEignores the cache entirely and never writes it, so every run re-executes. Reach for it to compare timings, or to confirm a result reproduces from cold rather than from a replayed output:TALIESIN_NO_CACHE=1
13.6 Diagrams / {js} cells don’t render
Both run offline: Mermaid is vendored and {js} cells have d3 + Plot bundled, so a
network is not what is missing. What still needs http(s) rather than file:// is a
{js} cell that import()s a relative helper module: the browser blocks module imports
on file://, so use preview or a served build.
13.7 “Port already in use”
preview automatically tries the next few ports and logs the substitution. Pass
an explicit port if you want a specific one: taliesin preview file.tmd 5000.
13.8 A broken {{< include >}} or extension
A missing include leaves an HTML comment breadcrumb and a diagnostics note, so it never fails silently.
13.9 Edits aren’t picked up
The watcher follows source/asset file types (.tmd, .bib, .css, images, …)
under the document’s directory and any {{< include >}}d file’s directory, and
skips build-output/VCS folders (_site, _book, .git). Editing a file outside
those won’t trigger a refresh.
13.10 The VS Code companion preview won’t connect
The companion spawns a plain taliesin preview <target> <port> and points its webview
at http://127.0.0.1:<port>/. <target> is the project root when the document
sits under a _site.yml (the webview then opens that page’s URL) and the file itself
otherwise. The preview binds loopback only, so there is no firewall hop to clear.
If the panel stays blank:
- Make sure
taliesinis on the path the extension uses. It runs whatever thetaliesin.pathsetting points at (defaulttaliesin); set it to an absolute path if yourPATHdoesn’t have one. - The extension waits up to a few seconds for the server to answer on its port. If
you see “taliesin preview did not answer”, the binary failed to start: run the
same
taliesin preview <target>in a terminal to read the real error. - The preview is reachable only from this machine. A request whose
Hostheader names anything but a loopback name is refused (the DNS-rebinding guard), so a proxy or tunnel in front of it will get a 403.
13.11 A large site previews or builds slowly
A first cold build of a site with many executed cells has to run them once; after
that, _freeze/ does the heavy lifting. Things that keep it fast:
Let the freeze cache warm up. The first run populates
_freeze/<page>.json; every laterbuild/previewof an unchanged page replays its outputs from disk and never boots a kernel. Don’t delete_freeze/between builds (it’s safe to commit if you want CI to skip execution), and don’t setTALIESIN_NO_CACHE, which defeats exactly this.Use
--no-execfor a structure-only pass. When you’re iterating on layout, navigation, or prose and don’t need fresh cell outputs, render the code cells as source and skip the kernel entirely:The pages render in full (chrome, links, math, figures already in the cache stay put as source) at a fraction of the cost, which is also the right call when you just want to eyeball a big site’s shape.