未定義の参照・ラベル重複

番号が出るはずの場所に ?? が出て、ログには LaTeX Warning: There were undefined references. ——LaTeX を使いはじめて最初にぶつかる不条理がこれです。参照が ?? になる理由は、LaTeX が 前回の実行が残した .aux ファイル を読んで番号を決めているから。つまり「なぜ 2 回コンパイルしないといけないのか」という古典的な疑問と、?? は同じ一枚のコインの裏表です。このページでは、その二段階の仕組み、Label(s) may have changedmultiply definedCitation ... undefined の読み分け、BibTeX と Biber を挟んだときの正しい実行順、そして——警告をひとつも出さずに間違った番号を印刷する \label の置き場所という、いちばん恐ろしい落とし穴を扱います。

なぜ 2 回コンパイルしないといけないのか

LaTeX は文書を一度上から下まで読み切るまで、後ろにあるものの番号を知らないからです。 3 ページ目で「第 7 章を参照」と書いても、第 7 章が何ページになるかはその時点では未定です。そこで LaTeX は、1 回目の実行で見つけた \label をすべて .aux ファイルに書き出し、2 回目の実行の冒頭でそれを読み込んで初めて番号を埋めます。だから 1 回目は必ず ?? になる——これはバグではなく設計です。しかも問題は循環しています。参照が ??(2 文字)から 12(2 文字)に変わると行分割が変わり、行分割が変わるとページ番号が変わり、ページ番号が変わると \pageref の値が変わる。LaTeX はこの循環を「収束するまで繰り返す」ことで解いており、収束したかどうかを判定して出すのが LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right. です。この一文が消えるまで回す、それが正しい運用です。

.aux の中身を一度覗いておくと、この仕組みが腑に落ちます。次は \section{The section}\label{sec:s} を含む文書を 2 回通した後の .aux の抜粋で、上が素の LaTeX、下が hyperref を読み込んだ場合です。\newlabel の引数は 5 つあり、1 番目が番号、2 番目がページ\ref は 1 番目を、\pageref は 2 番目を取り出しているだけです。hyperref を入れると 3 番目に見出しの文字列、4 番目にアンカー名が入ります。\nameref{sec:s} が「The section」と印字できるのはこの 3 番目のおかげで、\ref がクリックできるリンクになるのは 4 番目のおかげです。.aux が壊れたときに削除して作り直せばよい理由も、これを見れば分かります——中身は毎回作り直せる派生物にすぎません。

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}{}}

「何回回せば止まるのか」を人間が数えるのは無駄なので、その判断を自動化する道具が生まれました。latexmk です。ログを見て Rerun to get cross-references right が消えるまで、必要なら BibTeX や索引処理も挟みながら回してくれます。この道具の来歴は少し愉快で、TeX Live 2024 に同梱される latexmk の冒頭には、1992 年にミシガン大学の David J. Musliner が書いた go という名前のスクリプトが原型だと記されています。その後 Evan McLean が手を入れ、1998 年から John Collins が引き継いで、同梱版は 2024 年 1 月付のバージョン 4.83。「何回コンパイルすればいいのか」という素朴な苛立ちが、30 年以上メンテナンスされ続けるツールを生んだわけです。

警告の読み分け——どれが「まだ」でどれが「本当に無い」か

判定は簡単で、2 回目の実行でも同じ警告が残るかどうかだけを見ます。 1 回目にしか出ない警告は正常な動作、2 回目にも残る警告が本物の問題です。実際に \ref{nope}(存在しないラベル)と \ref{sec:real}(存在するラベル)を一つずつ含む文書を通すと、1 回目は両方が undefined として報告され、末尾に There were undefined references.Label(s) may have changed. Rerun to get cross-references right. が並びます。2 回目には sec:real が消え、nope だけが残り、Label(s) may have changed も消えます。カーネル latex.ltx を見ると、終了時のまとめ警告は There were undefined references の一つしかなく\ref の失敗も \cite の失敗も同じ旗を立てる仕組みです。つまり「undefined references」と書いてあっても、犯人が文献引用のこともあります。

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.
メッセージ意味やること
Label(s) may have changed番号がまだ収束していないもう一度コンパイルする。それだけ
Reference `x' ... undefined\label{x}.aux に無い2 回目にも出るなら綴りかラベルの位置を疑う
There were undefined references\ref\cite のどれかが解決していない上の個別警告を上から見る
Label `x' multiply defined同じラベルが 2 か所以上にある一意にする。2 回目以降にしか出ない
Citation `x' ... undefined.bbl にその文献が無いbibtexbiber を実行してから 2 回通す
rerunfilecheck: File ... has changedhyperref のしおり用ファイルが変わったもう一度コンパイルする

2 回流しても ?? が消えないとき

そのラベルが .aux に本当に書かれていない、ということです。 確認は一瞬で、.aux を開いて \newlabel{そのラベル名} を検索してください。無ければ \label 側の問題、あるのに ?? が出るなら \ref 側の綴りの問題です。ちなみに ?? が太字で印刷されるのは偶然ではありません。カーネルの \@setref\nfss@text{\reset@font\bfseries ??} と書いていて、見落とさせないために意図的に太字にしてあります。紙に印刷した草稿でも目に飛び込んでくるのはそのおかげです。以下、.aux にラベルが載らない典型的な理由です。

  • 綴り違い。 \label{fig:setup}\ref{fig:Setup} は別物。ラベル名は大文字小文字を区別します。
  • \includeonly で該当ファイルを除外している。 除外されたファイルの \label は書き出されないので、そこを指す \ref?? のまま。除外中は ?? が出るのが正常です。
  • .aux が古い、または壊れている。 前回の実行を中断した、クラスやパッケージを入れ替えた、といったときに起きます。.aux.toc.out を削除して 2 回通し直せば直ります。
  • \label が動く引数の中にある。 目次に流れる見出しやキャプションの引数に直接書くと、.toc に複製されて別の意味になることがあります。\caption、直後に置くのが安全です。
  • そもそも別の文書を組んでいる。 複数ファイル構成では、子ファイル単体をコンパイルしても親の .aux は更新されません。参照は必ず親から通します。
  • 文献のキーが .bib に無い。 これは ?? ではなく [?] として出ます。bibtex の出力に I didn’t find a database entry が無いか確認してください。

\label\caption の前に置くと、警告なしで番号が狂う

\label は必ず \caption の後ろに置いてください。前に置くとエラーも警告も出ないまま、まったく別の番号が印刷されます。 これは実測できます。\section を 3 つ置いた文書に図を 2 つ作り、1 つ目は \label\caption の前に、2 つ目は後ろに置いて 2 回コンパイルすると、.aux にはこう書かれました——\newlabel{fig:before}{{3}{1}{}{}{}}\newlabel{fig:after}{{2}{1}{}{}{}}。前置きした方は本来「図 1」であるべきなのに 3、つまり直前の節番号を拾っています。そして LaTeX の警告はゼロ件。PDF は問題なく生成され、本文には堂々と「図 3」と印刷されます。仕組みはこうです。\label は「いま有効なカウンタ」の値を記録しますが、そのカウンタを figure に切り替えて番号を進めるのは \caption の仕事(\refstepcounter の呼び出し)です。\caption より前では、直前に \refstepcounter を呼んだのは \section なので、節番号が記録されてしまいます。査読で「図 3 を参照とありますが図 3 は別のものです」と指摘されて初めて気づく類のバグで、\label\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——\itemequation\section*

規則はひとつだけです——番号を進める命令の直後に置く。 enumerate なら \item の直後、equation なら環境の中、節なら \section{…} の直後です。ここでも間違いは静かに起きます。\begin{enumerate} の直後(最初の \item より前)に \label{it:bad} を置いて 2 回コンパイルすると、.aux には \newlabel{it:bad}{{1}{1}{}{}{}} と記録されました——項目番号ではなく、直前の節番号の 1 です。警告はやはりゼロ。同じことが \section*{…} にも起きます。星付きの見出しは番号を進めないので、直後の \labelその前にあった番号付き節の番号 を拾います。星付き見出しを参照したいなら \phantomsectionhyperref)と \addcontentsline を併用するか、そもそも参照しない設計にしてください。ラベル名に fig:tab:sec:eq:it: のような接頭辞を付ける慣習が広く使われているのは見た目の整理のためだけではなく、\ref{fig:x} と書いた瞬間に「図を参照している」という意図が自分に見えるからです。番号が図のものになっていないとき、接頭辞があれば気づけます。

Label ... multiply defined は 2 回目からしか出ない

同じ \label{x} が 2 か所以上にあり、\ref{x} はそのうち一方(後に読まれた方)を指します。 そして重要な性質がひとつ——.aux を消した直後の 1 回目の実行では、この警告は 出ません。実測でも、.aux を削除して 1 回だけ通したときの multiply defined の件数は 0 でした。警告は 2 回目の実行の冒頭、LaTeX が .aux を読み込むときに、すでに定義済みのラベルをもう一度定義しようとして初めて出ます。つまり 「1 回コンパイルして警告が無かった」は重複ラベルが無い証拠になりません。文書末尾には LaTeX Warning: There were multiply-defined labels. というまとめも出ます。原因のほとんどはコピー&ペーストで、章ごとのファイルを複製したときや、\include で同じファイルを 2 回読み込んだとき、subfiles で親と子の両方から読まれたときに起きます。予防策は接頭辞をファイル単位で決めておくこと(intro:fig:setup のように)で、これなら別の章に貼り付けた瞬間に目で見て分かります。

Citation ... undefined と BibTeX/Biber の実行順

文献が [?] のままなら、必要な実行は 4 回です——pdflatexbibtexpdflatexpdflatex 参照が 2 回で済むのに文献が 4 回かかるのは、あいだに別のプログラムが挟まるからです。1 回目の pdflatex\cite されたキーの一覧を .aux に書き出し、ログには No file document.bbl.Citation ... undefined が並びます。次に bibtex.aux のキー一覧と .bib を突き合わせて .bbl(整形済みの文献リスト)を作ります。3 回目の pdflatex.bbl を初めて読み込み、各文献に番号を割り当てて .aux に書き出す——この時点ではまだ本文の \cite は解決していません。4 回目でようやく番号が入ります。実際に走らせたログでも、3 回目に Citation ... undefined が残ったまま Label(s) may have changed. Rerun to get cross-references right. が出て、4 回目で消えました。biblatex を使う場合は bibtex の代わりに biber を呼びますが、回数の構造は同じです。存在しないキーを引くと bibtex 側が Warning--I didn’t find a database entry for "missingkey" と教えてくれます——LaTeX ではなく bibtex の出力を読む ことが、このタイプの問題では決定的です。

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

\ref\eqref\cref\nameref\vref の使い分けと読み込み順

素の \ref は番号だけを出します。「図」「式」といった語や括弧は自分で書くことになり、そこが手作業だと必ずどこかでずれます。 そのずれを潰すための命令が層になって用意されていて、下の表がその一覧です。ただし 読み込み順に厳格な制約がありますcleverefhyperref より後、かつ varioref より後に読み込まなければなりません。順序を間違えると cleveref.sty が自分で診断してくれて、! Package cleveref Error: cleveref must be loaded after hyperref!. あるいは ! Package cleveref Error: cleveref must be loaded after varioref!. と表示されます(どちらも実際に再現しました)。正しい順序は hyperrefvariorefcleveref です。実務上の指針としては、新規の文書なら cleveref に統一するのが最も事故が少ない選択です。\cref{eq:e} は「eq. (1)」、\Cref{sec:s} は「Section 1」と、種類に応じた語を自動で付けてくれるので、章立てを入れ替えても文言が壊れません。逆に既存の共著原稿では、共著者が \ref を書き続けることを前提に、混在を許容するほうが現実的です。

命令出力例提供元・注意
\ref1カーネル。番号だけ。hyperref があればリンクになる
\pageref1カーネル。.aux の 2 番目の値、つまりページ番号
\eqref(1)amsmath。数式番号を括弧つきで出す
\crefeq. (1)cleveref。種類に応じた語を自動で付ける
\CrefSection 1cleveref。文頭用に語の頭文字を大文字にする
\autorefsection 1hyperref\cref より単純で、範囲の連結はできない
\namerefThe sectionnamerefhyperref が読み込む)。見出しの文字列を出す
\vref1 on the previous pagevarioref。同じページなら番号だけ、離れていれば「前ページ」等を足す
preamble
% load order matters: hyperref, then varioref, then cleveref
\usepackage{amsmath}
\usepackage{hyperref}
\usepackage{varioref}
\usepackage{cleveref}    % must come last of the three