Lint Rules

Reference catalog of every built-in Panache lint rule, the diagnostic codes each rule emits, severity, auto-fix availability, and configuration requirements.

This page documents every built-in lint rule that Panache ships with. Each section lists the rule’s configuration name (used in [lint.rules]), the diagnostic codes the rule may emit at runtime, severity, auto-fix support, and any extension or metadata requirements.

For a user-friendly introduction to the linter, CLI usage, and configuration, see the Linting guide.

Diagnostic format

Diagnostics are displayed in a compiler-style format:

severity[diagnostic-code]: message
  --> file:line:column

Components:

severity
error, warning, or info
diagnostic-code
The specific code emitted (e.g., undefined-reference-label). A single rule may emit several distinct codes.
message
Human-readable description of the issue
location
File path, line number, and column number

Some diagnostics include additional notes pointing at related locations:

warning[duplicate-reference-labels]: Duplicate reference definition 'link1'
  --> document.qmd:4:1
note: First defined here:
  --> document.qmd:3:1

Severity levels

Panache uses three severity levels:

error
Critical issues that prevent correct parsing or rendering
warning
Likely mistakes or best practice violations
info
Informational messages (currently unused, reserved for future use)

Fix safety

Auto-fixes carry a safety level that controls when panache lint --fix applies them:

safe
The fix preserves the document’s meaning and only tidies syntax. Safe fixes are applied by a plain --fix.
unsafe
The fix may change the document’s meaning (for example, deleting a key). Unsafe fixes are skipped by a plain --fix; pass --unsafe-fixes alongside --fix to apply them too. When a run leaves unsafe fixes unapplied, the CLI prints how many are available.

In the editor (LSP), unsafe fixes are still offered as individual quick fixes (labeled (unsafe)), but they are excluded from the aggregate “Fix all auto-fixable lint issues” action, mirroring the safe-by-default CLI behavior.

The “Auto-fix” field of each rule below notes when its fix is unsafe.

Rules

heading-hierarchy

Detects skipped heading levels that violate document structure best practices.

Severity
Warning
Auto-fix
Yes
Diagnostic codes
heading-hierarchy
Description
Headings should increment by at most one level (e.g., H1 → H2 → H3). Skipping levels (H1 → H3) makes the document structure unclear and can break table-of-contents generation.

Example violation:

# Main Title

### Subsection

Diagnostic:

warning[heading-hierarchy]: Heading level skipped from h1 to h3; expected h2
 --> document.qmd:3:1
  |
3 | ### Subsection
  | ^^^^^^^^^^^^^^

Auto-fix: Changes ### Subsection to ## Subsection.

empty-list-item

Detects list items whose content is empty: a bare marker with nothing after it.

Severity
Warning
Auto-fix
No
Diagnostic codes
empty-list-item
Description

An empty list item is almost always a placeholder the author forgot to fill in. The rule fires in two situations:

  1. A LIST_ITEM produced by the parser has no inline content (e.g. a bare - line between two non-empty bullets, or 1. with nothing after the period).
  2. A bare - on the line below a list item gets interpreted as a Setext H2 underline, silently merging the item with the previous line as a heading. (= underlines are not flagged because they don’t share the bullet-marker shape.)

Both cases are valid Markdown, but they tend to surprise readers.

Example violation:

- Item one
-
- Item three

Diagnostic:

warning[empty-list-item]: List item has no content
 --> document.md:2:1
  |
2 | -
  | ^

Resolution: Fill in the missing content, or delete the marker. No auto-fix is provided because the right choice (placeholder vs. removal) is an author-intent decision.

empty-values

Flags YAML block-mapping keys whose value is an implicit null: a key with nothing after the colon.

Severity
Warning
Auto-fix
Yes (unsafe). Deletes the empty key’s whole line. Marked unsafe because dropping a key changes the document’s data, so it is applied only with --unsafe-fixes.
Diagnostic codes
empty-values
Description

Modeled on yamllint’s empty-values. A key with no value (title: followed by a newline) parses as YAML null. That is occasionally intentional, but far more often a value the author forgot to fill in. The rule walks document frontmatter and hashpipe cell options alike (both embed the same YAML CST).

An explicit null is a deliberate value and is never flagged, so write title: null (or title: ~) when the empty value is on purpose. The rule covers block mappings only; flow mappings ({a: }) and block sequences are not flagged.

Example violation:

---
title:
author: Jane Roe
---

Diagnostic:

warning[empty-values]: Key `title` has an empty value (implicit null)
 --> document.md:2:1
  |
2 | title:
  | ^^^^^

Resolution: Supply a value, delete the key, or write an explicit null if the empty value is intentional. The auto-fix takes the “delete the key” path, but because that discards data it is unsafe and runs only under --unsafe-fixes.

consumer-divergence

Flags a plain YAML scalar whose resolved type or value differs across the document’s active YAML consumers—a real cross-toolchain ambiguity, not a style opinion.

Severity
Warning
Auto-fix
Yes (unsafe). Single-quotes the value, forcing a string under every consumer. Marked unsafe because if the author meant the boolean or integer, quoting changes the value for the YAML 1.1 stage, so it is applied only with --unsafe-fixes.
Diagnostic codes
consumer-divergence
Requirements
Quarto flavor (flavor = "quarto"). Only Quarto frontmatter is read under two different YAML versions; every other context has a single consumer and cannot diverge.
Description

A Quarto document’s frontmatter is parsed twice: by Quarto’s js-yaml (YAML 1.2 core) and by pandoc’s libyaml (≈ YAML 1.1). The two versions resolve some plain scalars to different values. This rule flags only those genuine divergences. Unlike yamllint’s truthy/octal-values, it stays silent when the value is unambiguous for the document’s consumers.

Detected divergences:

  • Booleans (the “Norway problem”). YAML 1.1 reads y, n, yes, no, on, off (and case variants) as booleans; YAML 1.2 core reads only true/false. So country: no is the boolean false to pandoc but the string "no" to Quarto.
  • Leading-zero integers. YAML 1.1 reads 0755/010 as octal; js-yaml reads them as strings (it accepts only 0o… octal). So the two stages disagree on mode: 0755 (integer 493 vs string).

Quoted, literal, and folded scalars are never flagged (their string tag is pinned), and neither are unambiguous values such as true, 42, 3.14, .inf, or plain words. The rule covers block mappings only; flow mappings and sequence items are not yet inspected.

Example violation:

---
country: no
mode: 0755
---

Diagnostic:

warning[consumer-divergence]: Key `country`: value `no` is the boolean `false` to pandoc (YAML 1.1) but the string "no" to Quarto's js-yaml (1.2)
 --> document.qmd:2:10
  |
2 | country: no
  |          ^^

Resolution: Quote the value to force a string under every consumer (country: 'no'), or write the explicit true/false (or canonical integer) you mean. The auto-fix takes the quoting path, but because that changes the value for the YAML 1.1 stage it is unsafe and runs only under --unsafe-fixes.

heading-eaten-attrs

Detects HTML comments that cause pandoc to silently drop a heading’s {...} attribute block.

Severity
Warning
Auto-fix
No
Diagnostic codes
heading-eaten-attrs
Requirements
extensions.header-attributes = true (the default for Pandoc, Quarto, and R Markdown flavors).
Description
Pandoc requires the {...} attribute block on a heading to be followed only by whitespace. When any non-whitespace content (including an HTML comment) follows the brace block, pandoc treats the braces as literal text, silently dropping the attributes and producing an auto-generated id like "title-.unnumbered". Cross-references that targeted the intended id then break with no diagnostic from pandoc itself.

Example violation:

# Bibliography {.unnumbered} <!-- TODO -->

Diagnostic:

warning[heading-eaten-attrs]: Comment on a heading line with `{...}` attributes;
pandoc treats the brace block as literal text when anything follows it on the line.
 --> document.qmd:1:30
  |
1 | # Bibliography {.unnumbered} <!-- TODO -->
  |                              ^^^^^^^^^^^^^
help: Move the comment to its own line before or after the heading.

Resolution: Move the comment to its own line. No auto-fix is provided because the right placement (above or below the heading, or deleting the comment entirely) is an author-intent decision.

heading-strip-comments-residue

Detects HTML comments adjacent to a heading’s {...} attribute block that would leave stray whitespace under pandoc --strip-comments.

Severity
Warning
Default
Off. Opt in via [lint.rules] heading-strip-comments-residue = true.
Auto-fix
No
Diagnostic codes
heading-strip-comments-residue
Requirements
extensions.header-attributes = true.
Description
When attributes still parse (the comment sits before the brace block), invoking pandoc with --strip-comments removes the comment text but leaves the surrounding whitespace. The resulting heading source has trailing or interior whitespace adjacent to the attribute block, which can subtly affect downstream tooling. This rule is opt-in because most authors do not use --strip-comments; enable it when your publishing pipeline does.

Example violation:

# Bibliography <!-- TODO --> {.unnumbered}

Resolution: Move the comment to its own line before or after the heading.

duplicate-reference-labels

Detects duplicate reference link, footnote, and cross-reference label definitions.

Severity
Warning
Auto-fix
No
Diagnostic codes
duplicate-reference-labels
Description
Each reference label, footnote ID, and cross-reference label must be unique within a document. Duplicate definitions cause ambiguity. Only the first definition is used, making the others ineffective. Cross-reference detection follows the active flavor’s extensions, so flavor-specific declaration syntax (for example bookdown (\#eq:label) under RMarkdown) is included.
Reference-link and footnote labels collide across files only when those files are merged into a single Pandoc pass. That happens for Quarto includes (include shortcodes, which splice the child into its parent) and for bookdown projects (_bookdown.yml), where chapters are concatenated. In those cases a footnote or reference label repeated across files is reported. Quarto renders each .qmd as its own document, so the same label in two independently-rendered files is not a conflict and is not flagged. Cross-reference labels (such as figure and section IDs) resolve across documents and are checked project-wide regardless.

Example violation:

See [link1] and [link2].

[link1]: https://example.com
[link1]: https://different.com

Diagnostic:

warning[duplicate-reference-labels]: Duplicate reference definition 'link1'
  --> document.qmd:4:1
note: First defined here:
  --> document.qmd:3:1

Resolution: Rename or remove the duplicate definition.

undefined-references

Detects reference links and footnotes that point to missing definitions.

Severity
Warning
Auto-fix
No
Diagnostic codes
undefined-reference-label, undefined-footnote-id
Description
Flags unresolved reference-style links (including shortcut and collapsed forms) and unresolved footnote references. This helps catch broken cross-references early in editing and CI.

Example violation:

See [missing][nope] and note[^missing].

[ok]: https://example.com

Diagnostic:

warning[undefined-reference-label]: Reference label '[nope]' not found
  --> document.qmd:1:15
warning[undefined-footnote-id]: Footnote '[^missing]' not found
  --> document.qmd:1:31

undefined-reference-label

Emitted when a reference-style link or cross-reference points to a label that has no matching definition. Cross-references using an extension prefix listed in crossref-prefixes are not validated, since their targets are defined by a mechanism Panache does not model.

undefined-footnote-id

Emitted when a footnote reference points to an ID that has no matching footnote definition.

On flavors with inline footnotes, a label containing whitespace ([^and a note about them]) is left to reversed-footnote-marker instead: pandoc never reads such a bracket as a footnote reference in the first place, so reporting a missing definition would be misleading.

undefined-anchor

Detects inline links whose #fragment destination has no matching anchor in the document.

Severity
Warning
Auto-fix
No
Diagnostic codes
undefined-anchor
Description

Flags [text](#fragment) links where #fragment does not match any anchor that will exist in the rendered output. Anchor sources include explicit {#id} attributes on headings, fenced divs, code blocks, spans, and chunk labels, plus auto-generated heading IDs (when the auto_identifiers extension is enabled). Matching is case-sensitive, mirroring how browsers resolve URL fragments.

Links with a path component (other.qmd#frag), absolute URLs (https://example.com#frag), and bare back-to-top links (#) are not flagged. In bookdown projects, sibling chapters are scanned because bookdown’s gitbook renderer rewrites cross-chapter anchors. Quarto books render each chapter to a separate HTML page and are not scanned cross-chapter.

When the citations extension is enabled, links of the form [text](#ref-citekey) are recognized as overriding a citation’s link text (Pandoc renders bibliography entries with id="ref-<citekey>"), so they resolve as long as @citekey appears somewhere in the document.

Anchors declared via raw HTML <a id="x"> or <a name="x"> are not currently inspected, so links to them may be flagged as undefined. <div id="x"> blocks are recognized.

Example violation:

# Real Heading {#real}

See [the typo](#reel).

Diagnostic:

warning[undefined-anchor]: Anchor '#reel' not found in document
  --> document.qmd:3:16

unused-definitions

Detects reference labels and footnote definitions that are declared but never referenced.

Severity
Warning
Auto-fix
No
Diagnostic codes
unused-definition-label, unused-footnote-id
Description
Flags unused reference definitions ([label]: ...) and unused footnote definitions ([^id]: ...). This helps keep documents tidy and avoids dead references that can accumulate over time. When project metadata is available (for example in Quarto/Bookdown project lint runs), usage is resolved across project documents to reduce cross-file false positives.

Example violation:

Text with one note[^1].

[^1]: Used note.
[^2]: Unused note.

[used]: https://example.com
[unused]: https://unused.example.com

Diagnostic:

warning[unused-footnote-id]: Footnote '[^2]' is never used
  --> document.qmd:4:1
warning[unused-definition-label]: Reference definition '[unused]' is never used
  --> document.qmd:7:1

unused-definition-label

Emitted when a reference definition ([label]: ...) is declared but never referenced anywhere in the document (or project, when project metadata is available).

unused-footnote-id

Emitted when a footnote definition ([^id]: ...) is declared but never referenced.

duplicate-yaml-anchor

Detects a YAML anchor name declared more than once within a single embedded YAML document (frontmatter or a hashpipe #| cell-options block).

Severity
Warning
Auto-fix
No
Diagnostic codes
duplicate-yaml-anchor
Description
A repeated &name anchor is valid YAML 1.2 — the last definition wins — but every real consumer (pandoc/libyaml, js-yaml, R yaml) accepts it, so it is never a parse error. A duplicate is almost always an accident, so the later declaration is flagged. Anchor scope is per document, so the same name reused after a --- document boundary is not a duplicate. Mirrors yamllint’s anchors: forbid-duplicated-anchors. No auto-fix is offered: whether to rename or drop the duplicate depends on intent.

Example violation:

---
base: &defaults
  timeout: 30
prod: &defaults
  timeout: 60
---

Diagnostic:

warning[duplicate-yaml-anchor]: YAML anchor `&defaults` is defined more than once
  --> document.md:4:1

unused-yaml-anchor

Detects a YAML anchor that is declared but never referenced by an alias within the same embedded YAML document (frontmatter or a hashpipe #| cell-options block).

Severity
Warning
Auto-fix
No
Diagnostic codes
unused-yaml-anchor
Description
An anchor (&name) with no matching *name alias in the same document is valid YAML but dead weight. Usage is resolved per document (aliases in a later document across a --- boundary do not count, matching YAML’s per-document anchor scope). Mirrors yamllint’s anchors: forbid-unused-anchors. No auto-fix is offered: removing the anchor may not be the intended resolution.

Example violation:

---
theme: &brand
  primary: blue
---

Diagnostic:

warning[unused-yaml-anchor]: YAML anchor `&brand` is never used
  --> document.md:2:8

unsupported-metadata-key

Detects a frontmatter mapping key that pandoc’s metadata layer refuses to convert, which aborts the whole document with an error pointing at the top of the metadata block rather than at the key.

Severity
Error
Auto-fix
No (quoting the key invents a key the author never wrote, and the usual real-world trigger wants the line moved out of the frontmatter instead)
Requirements
Requires a flavor whose frontmatter reaches pandoc as metadata — Pandoc, Quarto, or R Markdown. GFM, CommonMark, MultiMarkdown, mdsvex, and MyST frontmatter is not read by pandoc, so the rule is not registered there.
Diagnostic codes
unsupported-metadata-key
Description

Pandoc parses frontmatter with libyaml and then converts the result to metadata, where every mapping key must be a string. libyaml accepts a collection key ([a, b]: v, {x: 1}: v, an explicit ? - a- b: v) or an alias key (*anchor: v), so no YAML parse error fires — pandoc fails a step later with

Error parsing YAML metadata at (line 1, column 1):
Error in $: Non-string keys are not supported

and nothing renders. The reported position is the start of the metadata block, so on a long header it says nothing about which key is at fault. This rule points at the key.

Collection keys are rejected at any depth, including inside a top-level sequence, and an alias key is rejected even when its anchor holds a plain scalar (pandoc reports Non-string key alias there), so every alias key is flagged without resolving the anchor.

Non-string scalar keys are fine and never flagged: pandoc stringifies them, so 1: one, no: nope, and 2024-01-01: launch all convert. This is a key-shape rule, not a YAML 1.1 typing rule — for values whose type differs between pandoc and Quarto’s js-yaml, see consumer-divergence.

Only frontmatter is checked. Hashpipe #| cell options are read by js-yaml (Quarto) or the R yaml package (knitr), neither of which restricts key shape, and they never reach pandoc’s metadata layer.

A frontmatter block whose top level is a scalar or a sequence is not this error: pandoc declines to treat such a block as metadata at all and re-reads it as document content. Panache parses those the same way, so there is nothing to report.

Example violation:

---
title: Notes
[1]: https://example.com
---

Diagnostic:

error[unsupported-metadata-key]: sequence used as a metadata key is not
supported by pandoc
 --> document.qmd:3:1
  = note: pandoc converts frontmatter to metadata, where every key must be a
          string; it fails the whole document with `Non-string keys are not
          supported`
  = help: quote the key to make it a string, or move the line out of the
          frontmatter if it was not meant to be metadata

Correct forms:

The line was meant to be a link reference definition, so it belongs in the document body:

---
title: Notes
---

[1]: https://example.com

Or, if it really is metadata, quote the key so it is a string:

---
title: Notes
"[1]": https://example.com
---

citation-keys

Validates citation keys against loaded bibliographies and detects conflicts in inline bibliography entries.

Severity
Error for bibliography load or parse failures, Warning for undefined keys and duplicates
Auto-fix
No
Requirements
Requires extensions.citations = true in configuration
Diagnostic codes
bibliography-load-error, bibliography-parse-error, missing-bibliography-key, duplicate-bibliography-key, duplicate-inline-reference-id
Description
Checks that all cited keys ([@key]) exist in the configured bibliography files. Also validates inline bibliography entries for duplicates and conflicts.

Example violation (undefined key):

---
bibliography: refs.bib
---

See @smith2020 and @jones2021.

If jones2021 doesn’t exist in refs.bib:

warning[missing-bibliography-key]: Citation key 'jones2021' not found in bibliography
  --> document.qmd:5:20

Example violation (bibliography load error):

---
bibliography: nonexistent.bib
---
error[bibliography-load-error]: Failed to load bibliography nonexistent.bib: File not found
  --> document.qmd:1:1

When it runs: Only when document metadata includes bibliography configuration and the citation extension is enabled.

bibliography-load-error

Emitted when a configured bibliography file cannot be opened (missing, unreadable, etc.).

bibliography-parse-error

Emitted when a bibliography file is opened successfully but contains entries that fail to parse.

missing-bibliography-key

Emitted when a @cite reference does not match any key in the loaded bibliography. Built-in Quarto cross-references (@fig-, @tbl-, …) are exempt; for extension-injected prefixes such as pseudocode’s @algo-, list them under crossref-prefixes so they are treated as cross-references rather than citations.

duplicate-bibliography-key

Emitted when the same key appears more than once across loaded bibliography files. The diagnostic anchors to a bibliography declaration in the document’s own YAML frontmatter. A duplicate confined to a project-level bibliography (declared in _quarto.yml/_metadata.yml rather than the document) is a defect of that shared file, not of any one document: instead of being repeated for every inheriting document, it is reported once, anchored to the manifest’s own bibliography: value. This manifest check runs when a project directory is linted or when the manifest is targeted explicitly; a single-document lint stays quiet about the ambient manifest.

duplicate-inline-reference-id

Emitted when an inline bibliography entry collides with another inline entry or with a key from a loaded bibliography file.

citation-nonbreaking-space

Detects a breakable space (or a source line break) between text and a bracketed citation ([@key]), where line wrapping can strand the rendered citation — a lone [1] or (Smith 2020) — at the start of a line.

Severity
Warning
Auto-fix
Yes (replaces the space with a non-breaking one)
Requirements
Requires extensions.citations = true in configuration (default for the Pandoc, Quarto, and R Markdown flavors).
Diagnostic codes
citation-nonbreaking-space
Description

Pandoc renders the space before [@key] as an ordinary breakable space in every output format, so whether the citation lands at the start of a line is left to the final typesetting — and the author usually cannot know at writing time whether the document will render with a numeric or an author-year citation style. Tying the citation to the preceding word with a non-breaking space is safe under both. The rule flags a plain space, tab, or source line break directly before a bracketed citation (including suppressed-author citations like [-@key]). A citation that starts a paragraph, follows a hard line break, or is already tied with \ or a literal U+00A0 is not flagged. In-text citations (@key) are out of scope.

The auto-fix rewrites the gap to \ (Pandoc’s non-breaking space escape). When all-symbols-escapable is disabled, \ would not parse as an escape, so the fix inserts a literal U+00A0 character instead.

Example violation:

Some important fact [@smith2020].

Auto-fix output:

Some important fact\ [@smith2020].

unspaced-citation

Detects an in-text citation (@key) glued to the preceding word, where Pandoc leaves it as literal text rather than a citation — but only when key names a reference the document actually knows about.

Severity
Warning
Auto-fix
No
Requirements
Requires extensions.citations = true in configuration (default for the Pandoc, Quarto, and R Markdown flavors).
Diagnostic codes
unspaced-citation
Description

Pandoc’s notAfterString rule means an author-in-text @key is only recognized when its @ does not directly follow a word character. So word@smith2004 (and, for the same reason, an email like [email protected]) is parsed as ordinary text, and the intended citation silently fails to render. The rule flags such a glued @key so the mistake is caught at writing time.

To avoid false positives on email addresses, handles, and other incidental @ text, the rule fires only when the trailing key is a defined citation key: an entry in a loaded bibliography or an inline YAML references: entry. A key the document does not define is left alone.

There is no auto-fix because two distinct repairs are valid and the rule cannot know which the author intends: insert a space (word @smith2004) or wrap the key in brackets (word[@smith2004]). For a literal @ that should never be a citation, escape it as \@.

Example violation:

The result by work@smith2004 built on earlier findings in @smith2004.

chunk-label-spaces

Detects executable chunk labels containing whitespace (for example {r several words} or label="several words").

Severity
Warning
Auto-fix
No
Requirements
extensions.fenced-code-attributes = true (the default for Quarto and R Markdown flavors).
Diagnostic codes
chunk-label-spaces
Description
Labels with spaces are accepted by Quarto execution, but cross-references often fail to resolve reliably. Use a stable identifier such as several-words or several_words instead.

missing-chunk-labels

Detects executable chunks that do not define a label (either inline or hashpipe style).

Severity
Warning
Auto-fix
No
Requirements
A flavor with executable chunks (Quarto or R Markdown).
Diagnostic codes
missing-chunk-labels
Description
Labels facilitate debugging. Add a label with either a hashpipe option or inline label=my-chunk. The suggested hashpipe comment prefix matches the chunk language (#| for R or Python, //| for C++ or Rust, --| for SQL, and so on).

figure-crossref-captions

Detects figure cross-references that point to chunk labels without a figure caption option.

Severity
Warning
Auto-fix
No
Requirements
A flavor with executable chunks (Quarto or R Markdown).
Diagnostic codes
figure-crossref-captions
Description
Bookdown figure cross-references (\@ref(fig:...)) require a captioned chunk to create a resolvable figure label at render time. When the target chunk has a label but no fig-cap/fig.cap, the crossref will not resolve.

unknown-emoji-alias

Detects :alias: emoji shortcodes that are not recognized.

Severity
Warning
Auto-fix
No
Requirements
Requires extensions.emoji = true in configuration
Diagnostic codes
unknown-emoji-alias
Description
Checks parsed emoji aliases against the emoji shortcode dataset and warns when an alias is unknown.

Example violation:

Looks good :smile:, but this one is wrong :not-a-real-emoji:.

Diagnostic:

warning[unknown-emoji-alias]: Unknown emoji alias ':not-a-real-emoji:'
  --> document.qmd:1:40

html-entities

Detects malformed HTML named entity references in inline prose.

Severity
Warning
Auto-fix
No
Diagnostic codes
html-entities
Description

Pandoc and Quarto pass HTML named entities like &hellip; through to the output unchanged, so a typo (&ellips;) or a missing trailing semicolon (&numero instead of &numero;) silently produces wrong output. This rule flags three conservative cases:

  • &NAME; where NAME is not in the HTML5 named-entity table.
  • &NAME (no semicolon) where adding the semicolon would produce a known entity. This avoids firing on plain prose like “Tom & Jerry” or “AT&T”, since those words are not entity names.
  • &NAME (no semicolon, length ≥ 4) where NAME is one edit away from a known entity (e.g. &hellp&hellip;). Far-from-anything words are left alone to keep prose like “Procter &Gamble” quiet.

The rule deliberately ignores numeric character references (&#123;, &#xABCD;) for now and does not scan code spans, code blocks, raw HTML, inline/display math, link destinations, attributes, YAML metadata, comments, or the verbatim bodies of MyST code and math directives ({code-cell}, {code-block}, {code}, {math}).

Example violations:

This is &ellips; wrong.

Section &numero 5 of the report.

Diagnostics:

warning[html-entities]: Unknown HTML entity '&ellips;'
 --> document.qmd:1:9
  = help: did you mean '&hellip;'?

warning[html-entities]: HTML entity '&numero' is missing a trailing ';'
 --> document.qmd:3:9
  = help: write '&numero;' to encode the character

adjacent-footnote-refs

Detects footnote references placed back-to-back ([^a][^b]) where the rendered superscripts run together (e.g. footnotes 7 and 8 look like footnote 78).

Severity
Warning
Auto-fix
Yes (inserts a space between the references)
Requirements
Requires extensions.footnotes = true in configuration
Diagnostic codes
adjacent-footnote-refs
Description
When two footnote references appear with no intervening character, most renderers emit the superscript markers as a single visually-merged run. Inserting a single space between them keeps the markers distinct without changing the prose.

Example violation:

See the prior reports[^a][^b] for context.

Auto-fix output:

See the prior reports[^a] [^b] for context.

blank-line-in-inline-footnote

Detects an apparent inline footnote whose opening ^[ and closing ] are separated by a blank line.

Severity
Warning
Auto-fix
No (removing the blank line would merge paragraphs and change document structure based on inferred intent)
Requirements
Requires extensions.inline-footnotes = true in configuration
Diagnostic codes
blank-line-in-inline-footnote
Description

Pandoc inline footnotes may wrap across ordinary line breaks, but they cannot contain multiple paragraphs. A blank line ends the paragraph before pandoc finds the closing bracket, so the entire apparent footnote—including its ^[ and ] markers—is rendered as literal text.

The rule reports an unescaped ^[ at the end of a paragraph only when the paragraph after the blank line contains an apparent closing ]. This avoids warning about a lone ^[ used as literal notation.

Example violation:

This has an inline note.^[The note starts here.

It appears to continue here.]

blank-line-in-display-math

Detects standalone $$ display-math delimiters separated by a blank line.

Severity
Warning
Auto-fix
No (removing the blank line would merge paragraphs and change document structure based on inferred intent)
Requirements
Requires extensions.tex-math-dollars = true—a tex-math-* extension enabled by default for Pandoc, Quarto, and R Markdown flavors.
Diagnostic codes
blank-line-in-display-math
Description

Pandoc display math may span ordinary line breaks, but it cannot span a blank line. The blank line closes the opening paragraph before Pandoc sees the second delimiter, so both $$ markers are rendered as literal text in HTML and PDF output rather than as an equation.

The rule reports the opening delimiter only when the immediately following paragraph contains another standalone $$ line. It therefore leaves lone literal dollar markers alone.

Example violation:

$$
a

$$

Resolution: Remove the blank line to create a display-math block, or escape the dollar signs when they are intended as literal text. No auto-fix is offered because either interpretation may be correct.

footnote-swallowed-by-bracket

Detects inline footnotes (^[note]) followed immediately by [ or (, where pandoc consumes the note body as a link label and the footnote silently disappears.

Severity
Warning
Auto-fix
Yes (inserts a space after the footnote’s closing ]; safe for the [ form, unsafe for the ( form)
Requirements
Requires extensions.inline-footnotes = true in configuration
Diagnostic codes
footnote-swallowed-by-bracket
Description

Pandoc only reads ^[ as an inline-footnote opener when the closing ] is not followed by another bracket run. When a [ follows, the two bracket groups are parsed as a reference link and the whole span degrades to literal text — the ^[ markers show up verbatim in the output. When a ( follows, it is worse: [note](dest) becomes an ordinary link and the ^ is left behind as a stray caret, so the document renders cleanly and the lost footnote is easy to miss.

Inserting a single space after the footnote’s closing ] restores the footnote and leaves the following link intact. For the ( form the fix is marked unsafe, because a stray ^ in front of an intended link is an equally plausible reading; apply it with --unsafe-fixes once you have confirmed a footnote was meant.

Example violation:

Coordinates^[Notes on coordinates.][Figure 1](#fig-1) follow.

Auto-fix output:

Coordinates^[Notes on coordinates.] [Figure 1](#fig-1) follow.

reversed-footnote-marker

Detects [^...] brackets that pandoc will not read as a footnote reference, which usually means the inline-footnote marker was written backwards — [^note] where ^[note] was meant.

Severity
Warning
Auto-fix
Yes, but unsafe (swaps the [^ opener for ^[; the brackets currently render as something, so promoting them to a footnote changes the output). Withheld when a [ or ( follows the closing ], where the swap would not produce a footnote at all.
Requirements
Requires extensions.inline-footnotes = true in configuration
Diagnostic codes
reversed-footnote-marker
Description

Pandoc’s reference form takes a bare label: [^ has to be followed by label characters with no whitespace and a closing ] on the same line. Prose between the brackets breaks all of that, and the bracket run quietly degrades into something else — literal text, or a citation when the prose happens to contain an @key. Because nothing disappears from the rendered output, the missing note is easy to overlook.

A well-formed but undefined label ([^missing]) is a different problem and stays with undefined-footnote-id. Links whose text merely starts with a caret ([^text](dest)) are left alone: they render as working links, so a stray caret is the likelier reading.

The fix is marked unsafe because it changes what the document renders: the brackets stop being prose and become a footnote. Apply it with --unsafe-fixes once you have confirmed a note was meant.

No fix is offered when a [ or ( follows the closing ]. There the swap would leave the note just as absent while creating a footnote-swallowed-by-bracket defect — ^[note](dest) is a stray caret plus a link, and ^[note][ref] is literal text. Deciding where the note ends is the author’s call.

Example violation:

Coordinates are read-only [^and they are derived from the street number].

Auto-fix output:

Coordinates are read-only ^[and they are derived from the street number].

footnote-ref-in-footnote-def

Detects footnote references ([^id]) that appear inside a reference-style footnote definition body, where pandoc silently parses them as literal text instead of resolving the reference.

Severity
Warning
Auto-fix
No (the user must decide whether to inline the prose, restructure to lift the reference out of the definition body, or drop it)
Requirements
Requires extensions.footnotes = true in configuration (default for Pandoc, Quarto, R Markdown, and GFM flavors).
Diagnostic codes
footnote-ref-in-footnote-def
Description

Pandoc footnotes do not nest. Inside a [^x]: ... definition body, any [^id] reference is silently parsed as a literal Str and the would-be link disappears from the output with no warning. The same applies to references nested arbitrarily deep inside that body (inside emphasis, strong, strikeout, links, blockquotes, lists, or inline footnotes).

This rule surfaces the silent drop at lint time so the user notices before the document is rendered. After the parser fix that aligns panache with pandoc on this case, the inner references no longer appear as FOOTNOTE_REFERENCE nodes; the rule scans the definition body’s TEXT tokens directly for [^id] byte patterns, which naturally skips code spans, math, raw HTML, and other CST-distinct constructs.

References at the top level (outside any definition body) and inside a top-level inline footnote ^[...] are not flagged—pandoc resolves those normally.

Example violation:

Outer[^a].

[^a]: Body has [^b] ref and **bold [^c] inside** wrapper.

[^b]: B body.
[^c]: C body.

Diagnostic:

warning[footnote-ref-in-footnote-def]: Footnote reference '[^b]' inside a footnote definition body
                                       is silently dropped by pandoc (rendered as literal text)
 --> document.qmd:3:16
  = help: footnotes do not nest in pandoc; inline the prose, restructure to
          keep the reference outside the definition body, or remove it

footnote-after-image

Detects a footnote hanging off a standalone image, which keeps the image from being promoted to a figure and silently discards its caption.

Severity
Warning
Auto-fix
No (the user must decide whether the note belongs inside the caption or beside the figure as a separate paragraph)
Requirements
Requires extensions.footnotes = true in configuration (default for Pandoc, Quarto, R Markdown, and GFM flavors) and extensions.implicit-figures = true (default for Pandoc, Quarto, and R Markdown).
Diagnostic codes
footnote-after-image
Description

Pandoc’s implicit_figures promotes an image to a figure only when the image is alone in its paragraph. A trailing footnote breaks that condition, so

![A caption here.](img.jpg){#fig-1}
^[A note about the figure.]

parses as Para [Image, SoftBreak, Note] rather than Figure. Nothing errors: the caption text survives only as the image’s alt attribute and never renders as a caption. Under Quarto the consequence is louder, because the #fig- id no longer labels a figure and @fig-1 renders as an unresolved **?@fig-1**.

Both placements are flagged: the footnote on the line below (which pandoc reads as lazy continuation of the image’s paragraph) and the footnote on the same line. Reference-style footnotes ([^1]) demote the figure exactly as inline ones (^[...]) do, and are flagged alike.

The rule stays quiet when the demotion costs nothing. An image with neither caption text nor an id (![](img.jpg)) loses only an empty figure wrapper. A paragraph holding prose alongside the footnote is also skipped, because removing the footnote would not restore the figure there and the advice would be wrong.

There is deliberately no auto-fix. The two resolutions mean different things: moving the footnote inside the caption makes it part of the caption, while inserting a blank line restores the figure and leaves the note as a separate paragraph. Splicing a multi-line footnote body into a caption is also not a mechanical edit.

Example violation:

![A caption here.](img.jpg){#fig-1}
^[A note about the figure.]

Diagnostic:

warning[footnote-after-image]: footnote attached to a standalone image keeps it
from becoming a figure
 --> document.qmd:2:1
  = note: an image becomes a figure only when it is alone in its paragraph; the
          trailing footnote demotes it
  = note: the caption text will render as the image's alt attribute, not as a
          caption
  = note: the id no longer labels a figure, so cross-references to it will not
          resolve
  = help: move the footnote inside the caption, as in
          `![Caption. ^[note]](img.jpg)`, or separate it from the image with a
          blank line to keep the figure

Correct forms:

![A caption ^[A note about the figure.] here.](img.jpg){#fig-1}
![A caption here.](img.jpg){#fig-1}

^[A separate remark.]

stray-fenced-div-markers

Detects runs of three or more colons (:::, ::::, …) that appear inside inline text (paragraphs, tight list items, definition list bodies, table cells) instead of parsing as fenced div markers.

Severity
Warning
Auto-fix
No
Requirements
Requires extensions.fenced-divs = true in configuration (default for Pandoc, Quarto, and R Markdown flavors).
Diagnostic codes
stray-fenced-div-markers
Description

Pandoc fenced divs use ::: (or longer colon runs) for both openers (with an attribute or class) and closers (bare colons). Pandoc only treats ::: as a marker when it starts a line on its own; if the colons end up embedded in paragraph text—a stray closer with no matching opener, a closer accidentally glued to the previous line, or a marker with extra words after it—they silently render as ::: characters in the prose and the div either never opens or never closes.

Quarto’s runtime emits a warning when it sees stray :::, but exits 0, which makes the issue invisible to CI/Makefile workflows. This rule fills that gap by flagging the same condition at lint time.

The rule fires on any run of three or more : characters that survives as plain text inside paragraph-like inline content (paragraphs, tight list items, definition list bodies, table cells). Code spans (`:::`), indented code blocks, and raw HTML blocks are not flagged because the colons there are not inline text. Authentic prose mentions of ::: (writing about Pandoc syntax) should be wrapped in backticks anyway—runs of three or more consecutive colons are otherwise extremely rare in natural text.

Example violations:

::: warning
The fence count on the opener and closer don't match.
::::
::: {lang=en-US}
[contact Ms. Nebbercracker]{lang=en-US}:::
[]{#hmm}
::: {lang=zh-TW}
bla
:::

Diagnostic:

warning[stray-fenced-div-markers]: '::::' appears as text, not as a fenced div marker
 --> document.qmd:3:1
  = help: Pandoc only treats ':::' as a fenced div marker when it starts a line
          on its own (optionally followed by a class or attributes). Add a
          newline before it, or wrap it in backticks if it's intentional text

When a ::: run sits at the start of a line and the rest of the line forms a valid fence shape (opener or closer), but a preceding non-blank line pulls it into a paragraph, the diagnostic is sharpened to name the actual failure mode:

warning[stray-fenced-div-markers]: ':::' looks like a fenced div marker, but
the preceding line pulls it into a paragraph
 --> document.qmd:2:1
  = help: Insert a blank line above this line so Pandoc parses it as a fenced
          div instead of paragraph text

swallowed-list-marker

Detects a line that looks like a bullet or numbered list marker but was absorbed into the paragraph above, because no blank line separates them.

Severity
Warning
Auto-fix
No
Diagnostic codes
swallowed-list-marker
Description

Pandoc-markdown never lets a list interrupt a paragraph. A marker on the line directly below prose is therefore not a list at all—it is lazy continuation text, and reformatting splices the markers into the sentence above:

Reviewing this incident, the conclusion may only be
- train the registration staff
- scan the coordinate column

becomes one paragraph reading Reviewing this incident, the conclusion may only be - train the .... Pandoc produces exactly the same single Para, so this is an authoring trap rather than a formatting bug—but a silent one, which is what this rule fixes.

Under the CommonMark and GFM flavors bullets and ordered lists starting at 1 do interrupt a paragraph, so they parse as real lists and are never flagged. An ordered list starting at any other number still cannot interrupt, so 2. after a prose line is flagged there too, with a help note naming the CommonMark rule instead.

A run of consecutive marker lines produces a single diagnostic anchored at the first one, because the remedy is a single blank line above the run. Inserting one before every item would instead produce a loose list, which renders each item wrapped in its own paragraph.

The rule is deliberately narrower than Pandoc’s own marker syntax, to keep it quiet on ordinary prose. It recognizes -, *, and + bullets, and decimal markers of at most two digits followed by . or ). It does not flag alphabetical, roman, or example-list markers (a., iv., (@ok)), all of which occur often in normal sentences, nor longer numbers, so 2024. was a good year is left alone. Markers indented four or more columns, thematic-break shapes (- - -), and escaped markers (\-) are also skipped.

Example violation:

Reviewing this incident, the conclusion may only be
- train the registration staff
- scan the coordinate column

Diagnostic:

warning[swallowed-list-marker]: '-' looks like a list marker, but the preceding
line pulls it into a paragraph
 --> document.qmd:2:1
  = note: 2 consecutive lines here start with a list marker; all of them are
          reflowed into the paragraph above
  = help: Pandoc-markdown never lets a list interrupt a paragraph. Insert a
          blank line above this line to start a real list, or escape the marker
          ('\-') if it is meant as prose

Resolution: there is no auto-fix because two different intentions are plausible and the rule cannot tell them apart. If a list was meant, insert a blank line above the run; if the marker is prose (Prices range from $5 / - $10 depending on volume), escape it. A blank line is also not reliably sufficient on its own: any prose following the run would become lazy continuation of the final list item, and would need its own blank line too.

unspaced-list-marker

Detects a line consisting of -\ in paragraph text inside a list.

Severity
Warning
Auto-fix
No
Diagnostic codes
unspaced-list-marker
Description

A list marker needs whitespace after the hyphen. A line containing only -\, possibly indented, can look like an empty nested item, but the hyphen remains paragraph text. In Pandoc, the backslash before the newline adds a hard line break without creating another list item.

This rule is enabled by default in all flavors, including when the escaped-line-breaks extension is disabled. It flags the missing marker space whether the backslash becomes a hard break or stays literal text. The diagnostic covers only the hyphen and backslash.

Detection is limited to standalone -\ lines in list paragraphs. Ordinary soft line breaks, escaped hyphens (\-), code, and text inside inline spans are left alone. This is separate from swallowed-list-marker, which detects valid marker shapes absorbed into preceding prose because of a missing blank line.

Example violation:

- **Case background:**
  -\
- **Environmental concerns:**

Diagnostic:

warning: [unspaced-list-marker] Missing whitespace after '-': this line is paragraph text
 --> document.md:2:3
  = help: If you intended a list item, add a space after '-' and check its
          indentation. Escape the hyphen as '\-' if it is literal text

Resolution: add whitespace after the hyphen and check the indentation if a list item was intended. For a literal hyphen, escape it as \-. No automatic fix is offered because these choices express different intentions.

table-column-count

Detects a pipe table row carrying more cells than its delimiter row declares. Those cells are dropped when the table is rendered.

Severity
Warning
Auto-fix
No
Diagnostic codes
table-column-count
Description

A pipe table’s delimiter row owns its column count. Pandoc reads the count off that row and then pads or truncates every other row to it, so a header or data cell past the last column never reaches the output at all:

| a | b | c |
|---|---|
| 1 | 2 | 3 |

renders as a two-column table holding only a, b, 1, and 2. The c and 3 cells are silently discarded, which usually means the delimiter row was mistyped rather than that the cells were unwanted.

The opposite mismatch is harmless and is not flagged: a row shorter than the delimiter row is padded with empty cells, so no content is lost.

Under the gfm and commonmark flavors the mismatch is not a table at all — those dialects require the header to match the delimiter row cell-for-cell, and the whole run stays a paragraph — so this rule has nothing to flag there.

Example violation:

| a | b | c |
|---|---|
| 1 | 2 | 3 |

Diagnostic:

warning[table-column-count]: This row has 3 cells but the delimiter row declares
2 columns; the 1 extra cell is dropped when the table is rendered
 --> document.qmd:1:9

Resolution: there is no auto-fix because two different intentions are plausible and the rule cannot tell them apart. If the extra columns were meant, widen the delimiter row (|---|---|---|); if they were not, delete the surplus cells. The formatter leaves such a table byte-for-byte as written for the same reason: normalizing it would either discard the author’s text or widen the delimiter row and change what pandoc renders.

inline-math-line-break

Detects a top-level TeX row break (\\) in inline math.

Severity
Warning
Auto-fix
No (removing one backslash or moving the expression into a multiline construct requires knowing the author’s intent)
Requirements
Requires a tex-math-* extension (e.g. extensions.tex-math-dollars, default for Pandoc, Quarto, and R Markdown flavors).
Diagnostic codes
inline-math-line-break
Description

At the top level of inline math, \\ is a row-break control symbol. It does not escape the following backslash: \\mathrm is parsed as a row break followed by the ordinary letters mathrm, not as the command \mathrm. Some TeX engines accept a row break there, while other Markdown math renderers reject it, so the construct is not portable and is usually a doubled-backslash typo.

The rule does not report row breaks nested inside multiline constructs such as matrix, array, or \substack, and it does not report display math.

Example violation:

The differential is $\int f(x) \\mathrm{d}x$.

Resolution: If a command was intended, remove the extra backslash (\mathrm). If the break was intentional, use a display-math environment appropriate for multiple rows. No auto-fix is offered because those changes have different meanings.

math-syntax

Detects structural problems in the TeX content of inline ($...$) and display ($$...$$, \[...\], \begin{env}...\end{env}) math: unbalanced braces, unclosed or mismatched environments, and unescaped dollar tokens.

Severity
Error
Auto-fix
No
Requirements
Requires a tex-math-* extension (e.g. extensions.tex-math-dollars, default for Pandoc, Quarto, and R Markdown flavors). With no math extension enabled there are no math spans, so the rule never fires.
Diagnostic codes
math-unclosed-group, math-unexpected-close-brace, math-unclosed-environment, math-mismatched-environment, math-unexpected-end, math-unclosed-delimiter, math-unexpected-right, math-unexpected-dollar
Description

The math parser captures math content losslessly even when it is malformed, so a stray brace or unterminated environment never breaks parsing or formatting—but it is build-breaking downstream: quarto render to PDF hard-fails on an unclosed brace or mismatched environment, and MathJax/KaTeX silently drop the equation. That is why these ride at error severity. This rule surfaces the structural problems the parser already detected, with the diagnostic pointing at the offending byte (the unclosed {, the stray }, the mismatched \end, or an unescaped $).

The check is purely syntactic: TeX is a macro language, so the rule does not validate command names, argument arity, or semantics—only brace, environment, dollar, and \left/\right delimiter nesting. Because a macro can expand to braces or an environment the structural parser cannot see, valid TeX can occasionally look unbalanced; in that rare case disable the rule with [lint.rules] math-syntax = false or an ignore directive.

Example violation:

The mass-energy relation is $E = mc^{2$.

Diagnostic:

error[math-unclosed-group]: unclosed `{` group
 --> document.qmd:1:38
  |
1 | The mass-energy relation is $E = mc^{2$.
  |                                      ^

Resolution: Balance the braces or close the environment. No auto-fix is provided because the correct repair (which brace to add, where) is an author-intent decision.

math-unclosed-group

Emitted when a { is never closed before the end of the math content.

math-unexpected-close-brace

Emitted when a } appears with no matching {.

math-unclosed-environment

Emitted when a \begin{env} is never closed by a matching \end{env}.

math-mismatched-environment

Emitted when a \begin{a} is closed by \end{b} with a different name.

math-unexpected-end

Emitted when an \end appears with no open \begin.

math-unclosed-delimiter

Emitted when a \left delimiter is never closed by a matching \right (for example \left( x with no \right)).

math-unexpected-right

Emitted when a \right appears with no open \left.

math-unexpected-dollar

Emitted when an unescaped $ inside TeX math ends math mode prematurely.

quarto-schema

Validates document YAML frontmatter, code-cell options, and project config files (_quarto.yml, _metadata.yml) against Quarto’s machine-readable schema, flagging type mismatches and invalid enum values. (Unknown or misspelled keys are handled by the separate, opt-in quarto-schema-unknown-key rule.)

Severity
Warning
Auto-fix
No
Diagnostic codes
quarto-schema-type-mismatch, quarto-schema-invalid-enum
Requirements
Quarto flavor (flavor = "quarto"). Pandoc has no metadata schema, so the rule does not run for other flavors.
Description

Unlike pandoc—where metadata is an arbitrary mapping—Quarto ships a schema and validates against it. This rule reproduces the high-value parts of that check using a distilled copy of Quarto’s schema (vendored per quarto-cli release; see assets/quarto-schema/.panache-source). It covers document frontmatter (--- ... ---), code-cell options (#| ..., validated against the cell’s engine schema—knitr for R cells, jupyter otherwise), and project config files_quarto.yml (against the project-config schema) and _metadata.yml (directory metadata, against the frontmatter schema):

  • Type mismatches. A scalar whose resolved YAML 1.2 type does not match the schema (e.g. a boolean field set to a non-boolean) is flagged. String fields accept any scalar, since Quarto coerces.
  • Invalid enum values. A scalar outside a field’s allowed set is flagged.

The Quarto schema version the rule validates against can be pinned with [compat] quarto = "1.9". One version is currently bundled, so this is advisory.

Example violation:

---
toc: maybe
---

Diagnostic:

warning[quarto-schema-type-mismatch]: value should be a boolean
 --> document.qmd:2:6
  |
2 | toc: maybe
  |      ^^^^^

Project config files are validated wherever the linter encounters them: the CLI checks an explicit panache lint _quarto.yml (or _metadata.yml) target, and the LSP validates the manifests reachable from an open project document, publishing diagnostics on each manifest’s own file.

Opt-out: Like Quarto itself, this rule (and quarto-schema-unknown-key) honors validate-yaml: false. When a document’s frontmatter or a manifest sets it, no schema validation runs for that file—useful for filter-driven patterns the core schema rejects, such as a bibliography: map for multibib. A malformed-YAML parse error is still reported.

Limitations: Format-gated keys are accepted regardless of the active output format, and deeply nested options reached through a permissive anyOf branch may not be checked.

quarto-schema-unknown-key

Flags unknown or misspelled keys in document frontmatter, code-cell options, and project config files, validated against Quarto’s machine-readable schema.

Severity
Warning
Default
Off—opt in via [lint.rules] quarto-schema-unknown-key = true.
Auto-fix
No
Diagnostic codes
quarto-schema-unknown-key
Requirements
Quarto flavor (flavor = "quarto"). Pandoc has no metadata schema, so the rule does not run for other flavors.
Description

This rule is off by default because Quarto itself tolerates unknown keys at render time. Its schema objects are open so that pandoc passthrough and custom template metadata keep working. So flagging them is stricter than quarto render. It is still useful for catching silent-failure typos (a misspelled format block simply does nothing, and Quarto will not warn), so it is offered as an opt-in.

When enabled, on closed schema objects (e.g. a specific format’s option block) any undeclared key is flagged. On the open top level (which must allow pandoc passthrough and custom metadata) only a near-miss typo of a known key is flagged, with a “did you mean” suggestion.

Example violation (with the rule enabled):

---
forrmat: html
---

Diagnostic:

warning[quarto-schema-unknown-key]: unknown key `forrmat`; did you mean `format`?
 --> document.qmd:2:1
  |
2 | forrmat: html
  | ^^^^^^^

YAML diagnostics

Panache emits YAML diagnostics when embedded YAML content is invalid. These apply to both document frontmatter (--- ... ---) and executable chunk hashpipe options (#| ...).

yaml-parse-error

Severity
Warning
Auto-fix
No
Description
The YAML lexer or parser could not interpret the content (malformed flow sequences, unterminated strings, etc.).

Example (hashpipe):

```{r}
#| echo: [
1 + 1
```

Diagnostic:

warning[yaml-parse-error]: YAML parse error: ...
  --> document.qmd:2:10

yaml-structure-error

Severity
Warning
Auto-fix
No
Description
The YAML parsed successfully but its top-level shape is not valid for the context (for example, frontmatter that is not a mapping, or a hashpipe block that does not produce a mapping of options).