Fix cross-referenced longtable PDF failure with endfloat - #14742
Open
cderv wants to merge 3 commits into
Open
Conversation
Pandoc 3.8.1+ wraps a captionless table in `{\def\LTcaptype{none} ... }`
so it doesn't step the table counter. Quarto's longtable fixup adds its
own `\caption`, so it strips the definition but kept the braces, on the
assumption they were harmless.
They aren't. Pandoc's LaTeX template does `\makesavenoteenv{longtable}`,
and footnotehyper implements that with `env/longtable/before`/`after`
hooks that `\begingroup`/`\endgroup` around the environment. endfloat's
`\DeclareDelayedFloatFlavor*{longtable}{table}` reads the environment
into the .ttt file and terminates it with `\end{efloat@float}`, so the
`after` hook never fires and the `\savenotes` group stays open. Quarto's
trailing `}` is the first token to meet it, and LaTeX fails with
"Extra }, or forgotten \endgroup".
Drop the braces along with the definition, but only when they are
provably Pandoc's own wrapper: the preamble is the opening brace and the
definition and nothing else (anything else in the group is scoped by it
and would leak), the postamble is the closing brace and nothing else,
and the raw block holds a single longtable (the pattern spans first
\begin to last \end, so with two of them the braces belong to different
groups and removing both unbalances the output). Anything else falls
back to dropping the definition alone, exactly as before.
Rendered output is otherwise unchanged: table numbering,
cross-references and caption placement all come from Quarto's own
`\caption`.
Regression introduced in v1.9.17, which bumped Pandoc 3.6.3 -> 3.8.3.
Fixes #14741
The issue's reprex uses `knitr::kable(longtable = TRUE)`, which under Quarto defaults to the pipe format - so the cell emits a markdown table and Pandoc's writer produces the longtable. That is the shape 14741.qmd covers; this adds the engine version that goes through knitr for real, plus a second cell with `format = "latex"` for the raw-LaTeX path.
Collaborator
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
The full endfloat/footnotehyper chain lives in #14741 and the per-guard rationale in the 14741-raw-latex test doc; the inline comment only needs enough to read the code.
This was referenced Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Since Pandoc 3.8.1, a captionless
longtableis wrapped in a{\def\LTcaptype{none} … }brace group so it doesn't step the table counter. Quarto lifts the caption off any cross-referenceable table (#| label: tbl-) before Pandoc writes it, so every such table now takes that captionless path. Quarto supplies its own\captionand strips the\def\LTcaptype{none}(it would clash with thecaptionpackage), but until now kept the surrounding braces as harmless.They are not harmless. With
endfloatand\DeclareDelayedFloatFlavor*{longtable}{table}, the environment is read verbatim into the.tttfile and terminated with\end{efloat@float}, so theenv/longtable/afterhook thatfootnotehyper's\makesavenoteenv{longtable}installs never fires and its\savenotes\begingroupis left open. Quarto's trailing}is the first token to hit that open group, and compilation fails withExtra }, or forgotten \endgroup.This is a regression from the Pandoc 3.6.3 → 3.8.3 bump in v1.9.17. It is environment-dependent: the leaked group only exists when
footnotehyper.styis installed, which is why TinyTeX-based CI never caught it.Fix
src/resources/filters/customnodes/floatreftarget.luanow drops the braces along with the definition — but only when they are provably Pandoc's own wrapper: the preamble is exactly the opening brace and the\def, the postamble is exactly the closing brace, and the raw block holds a single longtable. Otherwise it falls back to stripping the definition alone, as before. That keeps hands off:\def\LTcaptype{figure}and the group scoping it,\setlength), which would leak if unbraced,Fixes #14741