Skip to content
Taliesin User Guide

13 Troubleshooting

How to read a diagnostic, 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 shows 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:

taliesin build . --check-only            # every located problem, nothing written
taliesin build . --check-only --strict   # …and advice fails the run too
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 suggests only names from its own vocabulary and never 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. Create a virtual environment in the project directory and install ipykernel into it:

python3 -m venv .venv && .venv/bin/pip install ipykernel

Taliesin finds a .venv in the project directory, or in a parent directory up to the repository root, with no configuration. To use a Python somewhere else, set TALIESIN_PYTHON to it. A plain pip install ipykernel into the system Python is refused on current Debian, Ubuntu and Homebrew Pythons (externally-managed-environment, PEP 668).

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. That includes a .venv created while the preview runs and a python: changed in _site.yml. TALIESIN_PYTHON is read from the environment the preview was started in, so a change to it needs a restart.

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.

A long cell therefore needs no configuration if it prints something occasionally. If a cell has to stay 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 wedged.

A cell can also ignore the interrupt, by installing its own SIGINT handler or by sitting inside a C extension that never checks for signals. Five seconds after the interrupt Taliesin then stops the kernel, and the page says “cell ignored the interrupt, so the kernel was stopped; the cells after it did not run”. The next run starts a fresh kernel and re-runs from the top.

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 recovers without hanging. The cell that was running when the kernel died shows “KernelDied: kernel process exited mid-cell”, and the console names it as the cell that crashed the kernel; each cell after it shows “kernel exited before this cell ran; it runs again next time”. 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 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 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), keyed by the cell’s code, every upstream cell’s code and the interpreter. An edit to your code always moves the key, and errors, #| cache: false cells and re-runs inside a warm kernel are never written, so an edit to code cannot bring back an old output. What can go stale is what the key does not see: a data file, an environment variable, a URL, the clock, a library upgraded in place. Mark a cell that reads one #| cache: false. The preview does not watch data files, so after editing data.csv save the .tmd (or use Restart kernel) to re-run it. If you suspect a stale output 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_CACHE ignores the cache entirely and never writes it, so every run re-executes. Use it to compare timings, or to confirm a result reproduces from cold rather than from a replayed output:

    TALIESIN_NO_CACHE=1 taliesin build report.tmd
    

13.6 Diagrams / {js} cells don’t render

Both run offline: Mermaid is vendored and {js} cells have d3 + Plot bundled, so a missing network is not the cause. A {js} cell that import()s a relative helper module does need http(s): 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 >}}

A missing include is left on the page as literal text, plus a diagnostics note, so it never fails silently.

13.9 Edits aren’t picked up

A save rebuilds the open pages that read the file: a page’s source, an {{< include >}}d file, a .bib, an image a page shows, a file a page links to. The watcher covers the project’s folder and skips generated and .-prefixed folders (_site, _book, _freeze, node_modules, .git, .venv). A stylesheet, a script or a data file the page loads in the browser is not read by the render: reload the tab to see a change to it.

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 no firewall needs opening. If the panel stays blank:

  • Make sure taliesin is on the path the extension uses. It runs whatever the taliesin.path setting points at (default taliesin); set it to an absolute path if your PATH doesn’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 Host header 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, their outputs come from _freeze/. Things that keep it fast:

  • Let the freeze cache warm up. The first run populates _freeze/<page>.json; every later build/preview of 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 set TALIESIN_NO_CACHE, which turns the cache off.

  • Use --no-exec for 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:

    taliesin preview ./site --no-exec
    

    The pages render in full (chrome, links, math) at a fraction of the cost, which also suits a quick look at a big site’s shape. Every code cell shows as source, including one whose output is already in _freeze/: cached outputs are not shown.