Skip to content

Fix crash loop on bib entries with the cite key on its own line - #31

Open
pevab wants to merge 1 commit into
micangl:masterfrom
pevab:fix/cite-key-on-separate-line
Open

Fix crash loop on bib entries with the cite key on its own line#31
pevab wants to merge 1 commit into
micangl:masterfrom
pevab:fix/cite-key-on-separate-line

Conversation

@pevab

@pevab pevab commented Aug 12, 2026

Copy link
Copy Markdown

Fixes #29.

Reproducer

@inproceedings{
key2025,
title={x}
}

BibTeX does not require the cite key to share a line with @type{, and this is exactly what OpenReview's "Cite" button exports — so it shows up in a lot of ML bibliographies. BibTeX itself accepts it, so it goes unnoticed until the editor falls over.

Cause

parse_type() matches the remainder of the @type{ line against [[\v^([^, ]*)\s*,\s*(.*)]], which requires both the key and a comma. On the entry above the remainder is "", so matchlist() returns {} and matches[2] / matches[3] are both nil.

The self.empty(matches[3]) guard is meant to catch that, but empty() has no branch for nil — it tests type(list) == "table" and == "string" and otherwise falls off the end returning nil. The guard fails open, parse_entry(self, nil) runs, and cmp_vimtex#count(v:null, '{') raises E706 (the try/catch there only handles E712).

Why it loops

Indexing runs on a repeating 50 ms timer, and the uncaught error aborts the callback before self.timer:stop() is reached. The timer keeps firing over progressively more damaged state, so a single bad entry produces an unbounded cascade of three alternating errors:

parser.lua:211  E706: Argument of count() must be a List, String or Dictionary
parser.lua:144  table index is nil                               (self.result[v.cite_key])
parser.lua:104  attempt to perform arithmetic on field 'lnum' (a nil value)

The change

Two parts, both needed:

  1. parse_entry() picks up the cite key from the first subsequent line that supplies one, when parse_type() couldn't find it.
  2. empty() returns true for nil, so the existing guards behave as intended.

Part 2 alone is not sufficient — I tested it. It silences the E706 but the entry is still stored with cite_key = nil, which just moves the crash to parser.lua:144 and the loop continues.

Verification

Against a real 55-entry bibliography containing one such entry, and a well-formed control:

entries result
unpatched, malformed entry crash loop (E706)
patched, malformed entry 55 clean
patched, entry reformatted onto one line 55 clean

The key sets from both patched runs are identical to what the unpatched parser produces on the well-formed file, so well-formed bibliographies are unaffected.

Separate suggestion, not in this PR

Independently of this parse bug, it may be worth wrapping the timer callback in a pcall that stops the timer on error. Right now any unexpected parse failure takes out the whole session rather than just the offending entry, which is what turned this into a hard crash instead of one skipped entry. Happy to add it here or open it separately if you'd like.

🤖 Generated with Claude Code

BibTeX does not require the cite key to share a line with "@type{", and
OpenReview's "Cite" export puts it on the next line:

    @inproceedings{
    key2025,
    title={x}
    }

parse_type() matches the remainder of the "@type{" line against
[[\v^([^, ]*)\s*,\s*(.*)]], which requires both the key and a comma. On
such an entry the remainder is empty, matchlist() returns {}, and
matches[2] / matches[3] are both nil.

The `self.empty(matches[3])` guard should catch that, but empty() has no
branch for a nil argument and falls through returning nil, so the guard
fails open. parse_entry() is then called with nil, and cmp_vimtex#count()
raises E706 (its try/catch only handles E712).

Because indexing runs on a repeating timer, the uncaught error aborts the
callback before timer:stop() is reached, so it fires again and again,
cascading into "table index is nil" at self.result[v.cite_key] and then
arithmetic on a nil self.lnum. One such entry makes the session unusable.

Fix by deferring the cite-key lookup to parse_entry() when parse_type()
cannot find it, and by returning true from empty() for nil so the
existing guards behave as intended.

Verified against a 55-entry bibliography containing one such entry: it
now indexes cleanly and produces a key set identical to the one the
unpatched parser produces after the entry is reformatted onto one line.

Fixes micangl#29

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

Crash upon load

1 participant