How to check LaTeX labels and cross-references
List every \label key defined anywhere in the project and every key referenced by the whole \ref family, then compare the two lists in both directions. Do it across every included file, not just the root. Then compile twice and read the log — and remember that a reference resolving to a number is not the same as it resolving to the right one.
Why a multi-file project is the hard case
In a single file this is easy: everything is in front of you and an editor will often resolve a reference on hover. A thesis is not a single file. The root document is a preamble and a list of \include lines, the labels live in chapter files, and the references that point at them live in other chapter files.
That produces a specific failure: a chapter is perfectly consistent on its own and broken in the assembled document. It also makes the obvious shortcut misleading, because compiling one chapter with \includeonly reports every reference into the rest of the thesis as undefined. Both the check and the compile have to see the whole project.
The four things that go wrong
Reference with no label
A key is referenced and nothing defines it. LaTeX prints two question marks where the number should be and warns about an undefined reference in the log. Renaming a label and updating only some of the places that point at it is the usual cause.
The same label defined twice
Two \label commands with one key. LaTeX warns about multiply-defined labels and the last one silently wins, so a reference resolves to a number nobody intended. Copying a chapter file as a template for the next chapter produces this reliably.
Label nothing refers to
Defined and never used. Harmless, and sometimes deliberate — a label left in place for a section you plan to reference later. Worth knowing about rather than fixing on sight, which is why it deserves a gentler status than the other two.
A label in the wrong place
The only one on this list that no tool can find. \label must follow the \caption inside a float, and must come after the sectioning command it belongs to — put it before, and it resolves perfectly to the previous number. Everything checks out and the reference is wrong.
Working through it
1. Search the whole project, not the root file
Labels are defined in the chapter files and referenced from other chapter files. Anything scoped to main.tex will report almost every reference as undefined and almost every label as unused, which is worse than not checking.
2. Include every command in the reference family
\ref is the obvious one. \eqref, \pageref, \autoref, \nameref and \vref all point at labels too, and if you use cleveref then \cref and \Cref do as well. Missing one of these from a search produces false reports in both directions.
3. Build the defined list and the referenced list separately
One key per line, sorted. This is the point at which duplicates become visible, because they end up adjacent.
4. Compare referenced against defined
Anything referenced with no definition is an undefined reference. Check the spelling and the case before assuming the label is gone; these keys are typed by hand and rarely follow a convention consistently.
5. Compare defined against referenced
Anything defined and never referenced is unused. Read the surrounding context before deleting — a label on a section you are about to cross-reference is not a mistake.
6. Check the definitions actually inside their floats
For every figure and table, confirm the \label sits after the \caption. This is a manual read, and it is the check most worth doing on a document you are about to submit.
7. Compile at least twice, then read the log
Cross-references are resolved through the .aux file written by the previous run, so a single pass on a changed document reports undefined references that are not really undefined. Run it again before believing the log, then look for undefined-reference and multiply-defined warnings.
8. Read the numbers in the PDF
Resolution is not correctness. Open the output and check that "Figure 3" is the figure you meant. Nothing static can do this step for you.
What “resolves” is worth
Two very different statements get collapsed into one word here, and keeping them apart is what stops a green result being misleading.
A label with that key exists
Decidable from the source alone, across the whole project, before you compile. This is what a static check can tell you, and it catches the renamed-label class of error completely.
The reference prints the right thing
Decided while the document is typeset. The number depends on counters, on float placement, on the packages you loaded and on where the label sits relative to its caption. Only the compiled PDF settles it, and only you can say whether the number is the one you meant.
There is a third category worth naming: a label produced inside a macro body, or generated by a package, is created when TeX expands it. A tool reading the source sees the definition, not the result — so a project that manufactures labels programmatically is one where static checking has less to say.
Where StudyRef helps
It resolves the include graph first, so labels and references are compared across the assembled project rather than file by file. Missing targets, duplicate definitions and unused labels are reported as three different things, with the file and line each came from — and a missing target may come with a suggestion when one defined key is clearly the closest match, but never when two are equally close, because guessing between them would be worse than saying nothing.
It does not compile, so it cannot tell you a float landed on the wrong page or that \ref printed a number you did not intend. The last two steps above stay yours.
Check labels across a whole project
Upload the project as a .zip so every included file is in scope. Requires an account on a paid plan.
Related
- Citation keys and BibTeX entries — the other resolution problem, with a database on the far side instead of the document.
- The pre-submission checklist — including the compile-and-read-the-log steps.