Skip to content

citeproc: warn with Q-23-2 at an unknown citation key instead of failing (#1111) - #1163

Merged
cscheid merged 3 commits into
mainfrom
bugfix/issue-1111
Nov 10, 2026
Merged

cscheid merged 3 commits into
mainfrom
bugfix/issue-1111

Conversation

@cscheid

@cscheid cscheid commented Nov 10, 2026

Copy link
Copy Markdown
Member

Fixes #1111. Braid: bd-s1kn2w1r.

Problem

Citing a key that isn't in the bibliography failed the render:

  • HTML: Error: Citation processing error: Reference 'missing2020' not found, with no Q-code and no location. Only the first missing key was reported.
  • Typst: Typst's own bibliography failed the compile with a generic Q-21-3, pointing at a line in the generated .typ file.

Pandoc (and Quarto 1) don't fail here. They warn citation X not found and render the citation as bold X?. This PR matches that for both formats and gives the warning a code and the key's location in the .qmd.

What changes

Output. A missing key renders as bold key? inside the citation's normal parentheses and delimiters, as Pandoc does: (Doe 2020; **missing2020?**), (see **missing2020?**). The item keeps its prefix and drops its suffix and locator. It takes no citation number in numeric styles, so the numbers of later references don't shift, and it sorts last within its citation. I checked each of these against Pandoc 3.12.1; the fixtures and observed output are in claude-notes/issue-reports/1111/ from the triage commit.

Warning. One Q-23-2 warning per missing key, at the key's span (just after the @) in its first citation:

Warning [Q-23-2]: Citation Key Not Found
No reference has the citation key `missing2020`, so the citation renders as `missing2020?`.

Code. Q-23-2 is new, in the existing citeproc subsystem (Q-23), with a catalog entry, a docs page, and a sidebar entry. The issue noted that citeproc's Q-10-2 collides with the template subsystem (bd-be2f7); this PR doesn't use the Q-10 codes, so the collision isn't made worse. bd-be2f7 still tracks fixing the remaining Q-10 codes in quarto-citeproc/src/error.rs.

Where

  • quarto-citeproc/src/eval.rs: an unknown item goes through unknown_item_output instead of returning ReferenceNotFound. Unknown ids are skipped when assigning citation numbers and sort last.
  • pampa/src/citeproc_filter.rs:
    • apply_citeproc_filter collects unknown ids. Only when there are some does it walk the document again for the key spans (Citation.id_source), so renders without missing keys pay nothing extra.
    • New check_citation_keys for the Typst path: it loads the bibliographies, warns, and replaces only the unknown items with **key?**. Known items stay a Cite, so Typst still resolves them (@doe2020 #strong[missing2020?]).
    • If any bibliography can't be read (e.g. Hayagriva .yml, which Typst reads but Q2 doesn't), it leaves the document alone and Typst reports as before.
  • quarto-core/src/stage/stages/user_filters.rs: runs check_citation_keys in the post pass for Typst when Q2's citeproc isn't in the filter list and margin citations aren't in use (the Lua margin handler needs the original Cite nodes). This runs after AstTransformsStage, so cross-reference @fig-x cites are already resolved and can't be mistaken for missing keys. The HTML citeproc filter already relies on the same ordering.

Not changed

  • CiteprocFilterError::ProcessingError still turns any remaining citeproc error into a string (the e.to_string() the issue pointed at). A missing key no longer reaches it. Carrying structured diagnostics through FilterError → PipelineError::stage_error is wider plumbing than this fix needs.
  • The ReferenceNotFound sites in types.rs (format_bibliography_entry*) still use location: None. They take a bare id from callers that have no source span, and the render path no longer hits them for a missing key.

Tests

  • 8 new unit tests in pampa (citeproc_filter::tests, unknown_key_* / key_check_*). They cover the bold marker and warning location, prefix kept and suffix dropped, one warning per key at its first citation, no citation number in a numeric style, and for the Typst check: partial replacement, a cite where every key is missing, separators between markers, and an unreadable bibliography. Written first; the citeproc ones failed with the old "must not fail the render" error.
  • 2 new end-to-end tests in quarto-core (unknown_citation_keys.rs) that render a .qmd to HTML and to Typst. They assert the Q-23-2 warnings, their index.qmd row and column, and the output markers.

Local run: cargo nextest run --workspace passed 19,220 of 19,221 tests. The one failure is engine::knitr::capture_tests::capture_classifies_pieces: the local R install reports the error class as rlang_error instead of simpleError. It's unrelated to this change, and main at the merge base is green in CI. Lint, fmt, the warnings-denied build, tree-sitter, and ts-packages steps of cargo xtask verify pass. The hub-client tests weren't run locally because the WASM package isn't built in this fresh worktree (--skip-hub-build); CI covers them.

Cross-platform: quarto-citeproc passes clippy --all-targets --target x86_64-pc-windows-msvc -D warnings. pampa and quarto-core can't be cross-checked from macOS because their C build scripts (tree-sitter, aws-lc-sys, wasmtime) need Windows headers. The change adds no platform-specific code; the tests compare paths by file name and use the same /mem runtime pattern as the existing tests.

🤖 Generated with Claude Code

t and others added 3 commits November 7, 2026 11:59
…ion (bd-s1kn2w1r)

Reproduced on main @ 250e2ed for HTML and Typst. Pandoc warns instead
of failing; per user decision the fix makes a missing key a coded,
located warning in both formats. Triage record and fixtures under
claude-notes/issue-reports/1111/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A citation key no reference defines used to fail the HTML render with an
uncoded "Citation processing error: Reference 'X' not found" and no
location, stopping at the first such key. On Typst, Typst's own
bibliography failed the compile and pointed at the generated .typ.

Match Pandoc's citeproc instead: warn and render the item as bold `X?`.

- quarto-citeproc: an unknown item renders as its prefix plus bold
  `id?` inside the citation's usual affixes and delimiters, drops its
  suffix, takes no citation number, and sorts last.
- pampa citeproc filter: one Q-23-2 warning per unknown key, at the
  key's span in the source (its first citation).
- Typst (citeproc off): a new check_citation_keys runs before Pandoc,
  warns the same way, and replaces only the unknown items, so the known
  keys stay native Typst citations. It skips the check when it can't
  read a bibliography (e.g. Hayagriva YAML) and leaves Typst to report.
- New catalog code Q-23-2 in the citeproc subsystem, with a docs page,
  avoiding the Q-10-* collision with the template subsystem (bd-be2f7).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@posit-snyk-bot

posit-snyk-bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@cscheid
cscheid merged commit 6e60cd4 into main Nov 10, 2026
20 of 21 checks passed
@cscheid
cscheid deleted the bugfix/issue-1111 branch November 10, 2026 22:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

citeproc: unknown citation key error has no Q-code and no source location

2 participants