Références non définies et labels dupliqués

Un ?? s’affiche là où devrait figurer un numéro, et le log annonce LaTeX Warning: There were undefined references. C’est la première absurdité apparente que rencontre tout utilisateur de LaTeX. Si une référence sort en ??, c’est que LaTeX tire ses numéros du fichier .aux laissé par la passe précédente. ?? et la question classique « pourquoi dois-je compiler deux fois ? » sont donc les deux faces d’une même pièce. Cette page traite de ce mécanisme à deux passes, de la distinction entre Label(s) may have changed, multiply defined et Citation ... undefined, de l’ordre d’exécution correct dès que BibTeX ou Biber entre en jeu, et — le piège le plus redoutable — de ce placement de \label qui imprime le mauvais numéro sans émettre le moindre avertissement.

Pourquoi faut-il compiler deux fois ?

Parce que LaTeX ignore le numéro de tout ce qui suit tant qu’il n’a pas lu le document de bout en bout une fois. Écrivez « voir le chapitre 7 » à la page 3 : à cet instant, la page où tombera le chapitre 7 n’est pas encore fixée. LaTeX écrit donc, lors de la première passe, chaque \label rencontré dans un fichier .aux, le relit au début de la deuxième passe, et ce n’est qu’alors qu’il remplit les numéros. La première passe donne donc forcément ?? : c’est le principe, pas un bogue. Et le problème est circulaire : quand une référence passe de ?? (deux caractères) à 12 (deux caractères aussi, mais différents), la coupure des lignes change ; si la coupure change, les numéros de page changent ; s’ils changent, les valeurs de \pageref changent. LaTeX résout le cercle en itérant jusqu’à stabilisation, et le message qui l’annonce est LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right. Relancer jusqu’à ce que cette phrase disparaisse : toute la discipline tient là.

Un coup d’œil dans un fichier .aux fait tout comprendre. Voici un extrait après deux passes d’un document contenant \section{The section}\label{sec:s} — d’abord en LaTeX nu, puis avec hyperref chargé. \newlabel prend cinq arguments, dont le premier est le numéro et le deuxième la page. \ref extrait simplement le premier ; \pageref extrait le deuxième. Avec hyperref, le troisième contient le texte du titre et le quatrième le nom d’ancre. \nameref{sec:s} peut imprimer « The section » grâce à ce troisième champ, et \ref devient un lien cliquable grâce au quatrième. Cela explique aussi pourquoi supprimer un .aux abîmé et le laisser se reconstruire est toujours sans risque : tout son contenu est dérivé et se régénère à chaque passe.

document.aux
% plain LaTeX: {number}{page}{}{}{}
\newlabel{sec:s}{{1}{1}{}{}{}}

% with hyperref: {number}{page}{title}{anchor}{}
\newlabel{sec:s}{{1}{1}{The section}{section.1}{}}

Compter les passes à la main est un gâchis d’être humain, aussi un outil est-il né pour décider à votre place : latexmk. Il lit le log et relance jusqu’à disparition de Rerun to get cross-references right, en intercalant au besoin BibTeX ou le traitement de l’index. Sa généalogie a du charme : l’en-tête du latexmk livré avec TeX Live 2024 indique que l’original était un script de 1992 nommé go, écrit par David J. Musliner à l’université du Michigan. Evan McLean l’a ensuite remanié, John Collins a repris la main en 1998, et la version distribuée porte le numéro 4.83, datée de janvier 2024. Un agacement tout simple — « combien de fois faut-il compiler ce truc ? » — fait vivre un outil entretenu depuis plus de trente ans.

Distinguer les avertissements : « pas encore » ou « vraiment absent »

Le test est simple : compilez deux fois et voyez si le même avertissement survit. Un avertissement qui n’apparaît qu’à la première passe est un comportement normal ; celui qui subsiste à la seconde est un vrai problème. Compilez un document contenant un \ref{nope} (label inexistant) et un \ref{sec:real} (label existant) : la première passe signale les deux comme indéfinis et se clôt sur There were undefined references. et Label(s) may have changed. Rerun to get cross-references right. À la deuxième passe, sec:real a disparu, seul nope demeure, et Label(s) may have changed s’est évanoui. Dans le fichier noyau latex.ltx, il n’existe qu’un seul message de fin, There were undefined references, et un \ref comme un \cite en échec lèvent le même drapeau. Un message parlant d’« undefined references » peut donc viser une citation.

log
% first run
LaTeX Warning: Reference `nope' on page 1 undefined on input line 3.
LaTeX Warning: Reference `sec:real' on page 1 undefined on input line 5.
LaTeX Warning: There were undefined references.
LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.

% second run -- sec:real is resolved, nope is a real problem
LaTeX Warning: Reference `nope' on page 1 undefined on input line 3.
LaTeX Warning: There were undefined references.
MessageSignificationQue faire
Label(s) may have changedles numéros ne sont pas encore stabilisésrecompiler, c’est tout
Reference `x' ... undefined\label{x} n’est pas dans le .auxs’il survit à la deuxième passe, suspecter l’orthographe ou le placement
There were undefined referencesun \ref ou un \cite n’a pas été résolulire les avertissements individuels au-dessus
Label `x' multiply definedle même label apparaît au moins deux foisle rendre unique. N’apparaît qu’à partir de la deuxième passe
Citation `x' ... undefinedcette entrée n’est pas dans le .bbllancer bibtex ou biber, puis recompiler deux fois
rerunfilecheck: File ... has changedle fichier de signets de hyperref a changérecompiler

Quand ?? survit à deux passes

Cela signifie que le label n’est réellement pas dans le .aux. La vérification prend une seconde : ouvrez le .aux et cherchez \newlabel{ suivi du nom. S’il manque, le problème est du côté du \label ; s’il est présent et que ?? persiste, c’est une différence d’orthographe du côté du \ref. Au passage, si ?? s’imprime en gras, c’est délibéré : le \@setref du noyau écrit \nfss@text{\reset@font\bfseries ??}volontairement gras pour qu’on ne puisse pas le manquer, ce qui le fait sauter aux yeux même sur un brouillon imprimé. Voici les raisons typiques pour lesquelles un label n’atteint jamais le .aux.

  • Une différence d’orthographe. \label{fig:setup} et \ref{fig:Setup} sont deux labels distincts ; les noms distinguent majuscules et minuscules.
  • Le fichier est exclu par \includeonly. Les labels d’un fichier exclu ne sont jamais écrits, et les renvois vers eux restent à ??. Tant que l’exclusion est active, ?? est le comportement correct.
  • Un .aux périmé ou corrompu. Cela fait suite à une passe interrompue ou à un changement de classe ou de package. Supprimez .aux, .toc et .out, puis compilez deux fois.
  • Le \label se trouve dans un argument mobile. Écrit directement dans l’argument d’un titre ou d’une légende, il peut être recopié dans le .toc et y signifier autre chose. La forme sûre est après \caption, hors des accolades.
  • Vous compilez le mauvais fichier. Dans un projet multifichier, compiler un fichier enfant seul ne met pas à jour le .aux du parent. Construisez toujours depuis le document parent.
  • La clé bibliographique n’est pas dans le .bib. Cela se manifeste par [?] et non ?? ; cherchez I didn’t find a database entry dans la sortie de bibtex.

Un \label placé avant \caption imprime le mauvais numéro, en silence

Placez toujours \label après \caption. Placé avant, il ne produit ni erreur ni avertissement — et imprime un numéro entièrement différent. Cela se mesure. Prenez un document avec trois \section et deux figures, mettez \label avant \caption dans la première et après dans la seconde, compilez deux fois, et le .aux indique : \newlabel{fig:before}{{3}{1}{}{}{}} et \newlabel{fig:after}{{2}{1}{}{}{}}. La première aurait dû être la figure 1, mais elle a enregistré 3, le numéro de la section précédente. Et LaTeX n’a émis aucun avertissement. Le PDF se construit sans accroc et le texte affirme tranquillement « figure 3 ». Le mécanisme : \label retient la valeur du compteur en vigueur, mais c’est \caption qui bascule sur le compteur figure et l’incrémente (il appelle \refstepcounter). Avant \caption, le dernier \refstepcounter exécuté est celui de \section, et c’est donc le numéro de section qui est noté. On découvre ce genre de bogue quand un relecteur écrit « vous renvoyez à la figure 3, mais la figure 3 montre autre chose » — d’où l’intérêt de retenir la règle \label immédiatement après \caption.

latex
\begin{figure}[h]
  \includegraphics{fig.pdf}
  \caption{The right way round}
  \label{fig:ok}          % after \caption -- records the figure number
\end{figure}

\begin{figure}[h]
  \label{fig:broken}      % before \caption -- records the SECTION number
  \includegraphics{fig.pdf}
  \caption{Silently wrong}
\end{figure}

\label dans les environnements : \item, equation, \section*

Il n’y a qu’une règle : placer \label immédiatement après la commande qui incrémente le compteur. Dans enumerate, juste après \item ; pour une équation, à l’intérieur de l’environnement ; pour une section, juste après \section{…}. Là encore, les fautes sont silencieuses. Placez \label{it:bad} juste après \begin{enumerate}, avant le premier \item, compilez deux fois, et le .aux enregistre \newlabel{it:bad}{{1}{1}{}{}{}} : non pas un numéro d’élément, mais le 1 de la section précédente. Toujours zéro avertissement. Il en va de même pour \section*{…} : un titre étoilé n’incrémente aucun compteur, si bien qu’un \label placé ensuite récupère le numéro de la dernière section numérotée qui le précède. Pour référencer un titre étoilé, associez-lui \phantomsection (de hyperref) et \addcontentsline, ou concevez le document de façon à ne pas le référencer. L’habitude répandue de préfixer les labels par fig:, tab:, sec:, eq:, it: ne tient pas qu’au rangement : écrire \ref{fig:x} rend votre intention visible sur la page — vous vouliez désigner une figure. Si le numéro imprimé n’est pas celui d’une figure, c’est le préfixe qui vous permet de le remarquer.

Label ... multiply defined n’apparaît qu’à partir de la deuxième passe

Le même \label{x} existe à deux endroits ou plus, et \ref{x} désigne l’un d’eux — le dernier lu. Une propriété mérite d’être connue : lors d’une première passe juste après la suppression du .aux, cet avertissement n’apparaît pas du tout. Mesuré ici, une passe unique sans .aux présent a signalé zéro multiply defined. L’avertissement surgit au début de la deuxième passe, quand LaTeX relit le .aux et se trouve à redéfinir un label déjà défini. Donc « j’ai compilé une fois sans voir d’avertissement » ne prouve pas que vos labels sont uniques. À la fin du document figure aussi un récapitulatif, LaTeX Warning: There were multiply-defined labels. La cause est presque toujours un copier-coller : un fichier de chapitre dupliqué, un même fichier inclus deux fois par \include, ou un document subfiles chargé à la fois par le parent et par l’enfant. L’habitude préventive est un préfixe par fichier — intro:fig:setup — car un label collé dans un autre chapitre saute alors aux yeux.

Citation ... undefined et l’ordre d’exécution de BibTeX/Biber

Si une citation reste bloquée sur [?], il faut quatre passes : pdflatex, bibtex, pdflatex, pdflatex. Les renvois en demandent deux, les citations quatre, parce qu’un autre programme doit s’intercaler. Le premier pdflatex écrit la liste des clés citées dans le .aux et journalise No file document.bbl. à côté de Citation ... undefined. Puis bibtex confronte cette liste au .bib et produit un .bbl, la bibliographie mise en forme. La troisième passe lit le .bbl pour la première fois, attribue un numéro à chaque entrée et l’inscrit dans le .auxà ce stade, les \cite du corps restent non résolus. Seule la quatrième passe les remplit. Dans un log réel, la troisième passe affichait encore Citation ... undefined avec Label(s) may have changed. Rerun to get cross-references right., et la quatrième était propre. Avec biblatex, on appelle biber au lieu de bibtex, mais la structure de la séquence ne change pas. Citez une clé inexistante et bibtex vous le dit : Warning--I didn’t find a database entry for "missingkey". Pour ce genre de problème, lire la sortie de bibtex plutôt que celle de LaTeX est déterminant.

terminal
pdflatex document     # writes the cited keys into document.aux
bibtex   document     # reads document.aux + refs.bib, writes document.bbl
pdflatex document     # reads the .bbl, numbers the entries
pdflatex document     # finally resolves every \cite in the text

# with biblatex, swap the second line for:
# biber document

Choisir entre \ref, \eqref, \cref, \nameref, \vref — et l’ordre de chargement

Un \ref nu n’imprime qu’un numéro. Le mot « figure » ou « équation » et les parenthèses éventuelles vous incombent, et tout ce qui est tapé à la main finit par se désynchroniser. Une couche de commandes existe pour combler ce manque, recensée dans le tableau ci-dessous — mais l’ordre de chargement est strict : cleveref doit venir après hyperref et après varioref. Si l’ordre est faux, cleveref.sty se diagnostique lui-même : ! Package cleveref Error: cleveref must be loaded after hyperref!. ou ! Package cleveref Error: cleveref must be loaded after varioref!. (les deux ont été reproduits ici). L’ordre correct est hyperrefvariorefcleveref. En pratique, pour un document neuf, s’en tenir à cleveref occasionne le moins d’accidents : \cref{eq:e} donne « eq. (1) » et \Cref{sec:s} donne « Section 1 », le mot adéquat venant automatiquement selon le type de cible, si bien qu’un remaniement de la structure ne laisse jamais de formulation périmée. Dans un manuscrit collectif hérité, en revanche, il est plus réaliste d’accepter le mélange et de compter sur des coauteurs qui continueront d’écrire \ref.

CommandeExemple de sortieFourni par / mise en garde
\ref1le noyau ; le numéro seul. Devient un lien si hyperref est chargé
\pageref1le noyau ; le deuxième champ du .aux, c’est-à-dire la page
\eqref(1)amsmath ; un numéro d’équation avec ses parenthèses
\crefeq. (1)cleveref ; fournit le mot correspondant au type de cible
\CrefSection 1cleveref ; forme capitalisée pour un début de phrase
\autorefsection 1hyperref ; plus simple que \cref, ne sait pas grouper des plages
\namerefThe sectionnameref, chargé par hyperref ; imprime le texte du titre
\vref1 on the previous pagevarioref ; le numéro seul sur la même page, avec une mention de page si la cible est ailleurs
preamble
% load order matters: hyperref, then varioref, then cleveref
\usepackage{amsmath}
\usepackage{hyperref}
\usepackage{varioref}
\usepackage{cleveref}    % must come last of the three