From 0c917f72b248a5de58ac6439abcb7697d9ae7660 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Fri, 29 May 2026 18:26:03 +0200 Subject: [PATCH] Detect luaotfload font-fallback crash and surface an actionable error On recent TeX Live (luaotfload v3.29), setting a font fallback (mainfontfallback/monofontfallback/...) crashes LuaLaTeX before a PDF is produced: luaotfload's fallback resolver dereferences a nil font. Quarto only showed a generic "compilation failed" with no usable signal, because the crash is a Lua runtime error with no standard "! ...Here is how much" block for the log parser to key off. Detect the crash signature in findLatexError and return a message that names the fallback options, explains it is a known upstream luaotfload bug on this TeX Live, and points to the single-font workaround. Upstream report: https://github.com/latex3/luaotfload/issues/331 See #14553. --- news/changelog-1.10.md | 1 + src/command/render/latexmk/parse-error.ts | 14 ++++++++++++ tests/unit/latexmk/parse-error.test.ts | 28 ++++++++++++++++++++++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/news/changelog-1.10.md b/news/changelog-1.10.md index e59c72deb4f..41f2c1fe058 100644 --- a/news/changelog-1.10.md +++ b/news/changelog-1.10.md @@ -25,6 +25,7 @@ All changes included in 1.10: ### `pdf` - ([#13588](https://github.com/quarto-dev/quarto-cli/issues/13588)): Fix Lua error when rendering PDF with `reference-location: margin` and a footnote alongside a figure with `fig-cap`. (author: @mcanouil) +- ([#14553](https://github.com/quarto-dev/quarto-cli/issues/14553)): Report a clear, actionable error when a font fallback (`mainfontfallback`, `monofontfallback`, ...) crashes LuaLaTeX on recent TeX Live, pointing to a single-font workaround, instead of a generic compilation failure. ### `typst` diff --git a/src/command/render/latexmk/parse-error.ts b/src/command/render/latexmk/parse-error.ts index 09cb24949a3..0e7002ed97f 100644 --- a/src/command/render/latexmk/parse-error.ts +++ b/src/command/render/latexmk/parse-error.ts @@ -205,10 +205,24 @@ export function findMissingHyphenationFiles(logText: string) { const kErrorRegex = /^\!\s([\s\S]+)?Here is how much/m; const kEmptyRegex = /(No pages of output)\./; +// luaotfload's font-fallback resolver crashes on recent TeX Live (luaotfload +// v3.29): when a fallback is set (mainfontfallback / monofontfallback / ...), +// define_font of the internal `;-fallback` name returns nil and +// luaotfload-fallback.lua dereferences it. The crash is a Lua runtime error +// with no `! ...Here is how much` block, so the generic extraction below finds +// nothing — detect it directly and give actionable guidance. +// Upstream: https://github.com/latex3/luaotfload/issues/331 +const kLuaotfloadFallbackCrash = + /luaotfload-fallback\.lua:\d+: attempt to index a nil value/; + export function findLatexError( logText: string, stderr?: string, ): string | undefined { + if (kLuaotfloadFallbackCrash.test(logText)) { + return "A font fallback (e.g. 'mainfontfallback' or 'monofontfallback') triggered a known luaotfload bug on this TeX Live version, which crashes LuaLaTeX before a PDF is produced. Until it is fixed upstream, set a single font that covers the required glyphs (e.g. 'monofont: JuliaMono') instead of a fallback list. See https://github.com/latex3/luaotfload/issues/331"; + } + const errors: string[] = []; const match = logText.match(kErrorRegex); diff --git a/tests/unit/latexmk/parse-error.test.ts b/tests/unit/latexmk/parse-error.test.ts index 4771a6d7b40..288362f90d3 100644 --- a/tests/unit/latexmk/parse-error.test.ts +++ b/tests/unit/latexmk/parse-error.test.ts @@ -5,7 +5,7 @@ * */ -import { findMissingFontsAndPackages, findMissingHyphenationFiles } from "../../../src/command/render/latexmk/parse-error.ts" +import { findLatexError, findMissingFontsAndPackages, findMissingHyphenationFiles } from "../../../src/command/render/latexmk/parse-error.ts" import { unitTest } from "../../test.ts"; import { assert } from "testing/asserts"; @@ -79,6 +79,32 @@ unitTest("Detect missing files with `findMissingFontsAndPackages`", async () => cwd: () => "unit/latexmk/" }) +// deno-lint-ignore require-await +unitTest("Detect luaotfload font fallback crash and surface an actionable hint", async () => { + // The luaotfload font-fallback resolver crashes on recent TeX Live when a + // fallback font is set (mainfontfallback/monofontfallback): define_font of the + // internal `;-fallback` name returns nil and luaotfload-fallback.lua + // dereferences it. There is no `! ...Here is how much` block, so the generic + // error extraction finds nothing — we want a specific, actionable message. + // Upstream: https://github.com/latex3/luaotfload/issues/331 + const fallbackCrashLog = + `luaotfload | db : Reload initiated (formats: otf,ttf,ttc); reason: Font "Latin Modern Roman;-fallback" not found. +luaotfload | resolve : sequence of 3 lookups yielded nothing appropriate....texmf-dist/tex/luatex/luaotfload/luaotfload-fallback.lua:50: attempt to index a nil value (local 'f'). +! ==> Fatal error occurred, no output PDF file produced!`; + + const message = findLatexError(fallbackCrashLog) ?? ""; + assert( + message.includes("font fallback"), + `Expected a font-fallback hint, got: "${message}"`, + ); + assert( + message.includes("luaotfload/issues/331"), + `Expected the upstream issue link, got: "${message}"`, + ); +}, { + cwd: () => "unit/latexmk/" +}) + unitTest("Detect missing hyphenation with babel warnings", async () => { // Test backtick-quote format (old format) const logWithBacktick = `Package babel Warning: No hyphenation patterns were preloaded for