Missing \begin{document}

! LaTeX Error: Missing \begin{document}. は、LaTeX のエラーのなかでも珍しく 自分について嘘をつく メッセージです。このエラーが出た文書の大半には \begin{document} がちゃんと書いてあります。真相はこうです——LaTeX は \begin{document} があるかどうかを一度も確認していません。確認しているのは「段落が始まったかどうか」だけで、プリアンブルで段落が始まってしまった瞬間に、この文面を出すよう仕掛けてあるのです。つまりこのエラーは「\begin{document} が無い」ではなく「\begin{document} より前に、印字されるものが現れた」と読むのが正しい。このページでは、その仕掛けを LaTeX 本体のソースで確認したうえで、迷い込んだ本文、プリアンブルに置いた \maketitle、そして目に見えない 1 バイトという 3 系統の原因を順に見ていきます。

なぜこのメッセージは嘘をつくのか

LaTeX はプリアンブルの \everypar\@nodocument を仕込んでおり、段落が始まった瞬間にそれが発火します。 \everypar は「段落が始まるたびに実行される」という TeX の仕掛けで、段落は「印字されるものが現れた瞬間」に始まります。LaTeX 本体 latex.ltx の該当箇所には、開発者自身のコメントがそのまま残っています。

latex.ltx
\gdef\@nodocument{%
  \@latex@error{Missing \protect\begin{document}}\@ehd}

\everypar{\@nodocument} %% To get an error if text appears before the
\nullfont               %% \begin{document}

\begin{document} より前に文字が現れたらエラーにするため」——コメントがそう言い切っています。\begin{document} の仕事はこの罠を解除することであって、罠は \begin{document} を探しているわけではありません。すぐ下の \nullfont も同じ思想で、プリアンブルの間は「文字を一つも持たないフォント」が現在フォントになっています。うっかり印字されても紙面に何も出ないようにしてあるわけです。 だから直し方も決まっています——探すべきは \begin{document} ではなく、その前にある印字されるものです。ただし、その行が本当に存在して綴りも合っているかは一度だけ確かめてください。まれではありますが、\begin{document} が本当に無い場合も同じメッセージになります。確認が済んだら、あとはもう \begin{document} を見なくて構いません。

ログは犯人の 1 文字をそのまま見せている

l.NN の行は、原因となった 1 文字のちょうど後ろで折り返されます。 TeX はエラー行を「読んだ部分」と「まだ読んでいない部分」に割って上下に並べるので、上の行の末尾がそのまま犯人です。単語の途中で切れているように見えるのはそのため——下の例では、3 行目の頭の T が「印字されるもの」の第一号として段落を開始させ、そこで罠が発火しています。この文書には \begin{document} がちゃんと 4 行目にあります。

terminal
! LaTeX Error: Missing \begin{document}.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...

l.3 T
     his line is body text by mistake.

プリアンブルで「印字されるもの」とは何か

プリアンブルに置いてよいのは、値を「登録する」だけで何も出力しない命令です。 \usepackage\title\author\date\newcommand、各種の設定——これらは情報をしまうだけなので段落を始めません。逆に、紙の上に何かを置こうとするものはすべてこのエラーになります。境界線は「見た目が本文っぽいか」ではなく「出力するか」なので、\title{論文題目} は安全で \maketitle は危険、という一見ふしぎな区別が生まれます。

  • 本文が紛れ込んだ——\documentclass の下に書きかけの一文が残っている、あるいは % を打ち忘れてコメントのつもりの一行が本文になっている。もっとも多い原因です。
  • \maketitle がプリアンブルにある——\title と並べて書いてしまう事故が頻発します。\maketitle\begin{document}に置きます。
  • 文字を出すマクロを呼んでいる——プリアンブルで \today や、自分で定義した「文字列を返すだけ」のマクロをうっかり単独で書いた場合。定義するのは安全、呼ぶのが危険です。
  • 記号や約物が 1 個はみ出している——\usepackage{amsmath} の直後の ~ や、閉じ忘れた括弧の外に残った文字など。} が 1 個余った場合は代わりに ! Too many } のエラーになります。
latex
\documentclass{article}
\title{My Paper}   % safe: records a value, prints nothing
\author{Me}       % safe
\maketitle        % ! LaTeX Error: Missing \begin{document}.
\begin{document}
\maketitle        % this is where it belongs
\end{document}

見えない 1 バイトが原因のとき

プリアンブルを何度読んでも印字されそうなものが見つからないなら、犯人は非改行空白 U+00A0 である可能性が高いです。 LaTeX の utf8enc.dfu には \DeclareUnicodeCharacter{00A0}{\nobreakspace} という行があり、U+00A0 は「印字される空白」として定義されています。つまり れっきとした出力なので、プリアンブルにあれば段落が始まってしまうわけです。しかも画面上は普通の空白と区別がつきません。ウェブページや PDF からコピーした \usepackage 行、Mac の Option+Space、フランス語配列のキーボードなどから、ごく自然に紛れ込みます。この場合、l.NN の上の行は 末尾に何も見えない という特徴的な形になります。

文字入り込む経路プリアンブルに置いたときの挙動
U+00A0非改行空白。ウェブや PDF からのコピー、Mac の Option+Space、仏語配列\nobreakspace として印字されるため、このエラーが出る。どのエンジンでも同じ
U+3000全角空白。日本語入力のまま空白を打った、和文の引用からのコピーxelatexlualatex ではこのエラー。pdflatex では Unicode character not set up という別のエラー
U+FEFFBOM。Windows の一部エディタが UTF-8 保存時に先頭へ付けるTeX Live 2024 では pdflatexxelatexlualatex のいずれも無視する。原因ではない

表の最後の行は、よく出回っている助言をひとつ否定しています。BOM はこのエラーの原因ではありません。 TeX Live 2024 で試すと、先頭に BOM を付けたファイルは pdflatexxelatexlualatex のいずれでも警告すら出さずに通ります(file コマンドで UTF-8 (with BOM) text と確認できる状態でも同じです)。「BOM なしで保存し直せ」という助言に従っても直らないのはこのためで、時間を使うなら U+00A0 を探すほうが当たります。ついでに \usepackage[utf8]{inputenc} も、いまは書く必要がありません——LaTeX News 28(2018 年)で UTF-8 が既定の入力エンコーディングになりました。文字コードそのものの扱いは「文字コードと改行」のページにあります。

紛れ込んだバイトを実際に見つける

プリアンブルは本来すべて ASCII のはずなので、「ASCII でない文字を含む行」を機械的に探せば一発です。 ただし文書全体に掛けると、日本語や中国語の本文が全行ヒットして役に立ちません。\begin{document} までで切ってから探すのがコツです。行が特定できたら sed -n l でその行のバイトを見ます——U+00A0 なら \302\240 と表示され、正体が確定します。あとはその 2 バイトを普通の空白に打ち直すだけです。

bash
# list preamble lines that contain a non-ASCII character
sed -n '1,/begin{document}/p' paper.tex | LC_ALL=C grep -n '[^ -~]'

# reveal the bytes on a suspect line (U+00A0 shows up as \302\240)
sed -n '3p' paper.tex | sed -n l

# check whether the file carries a byte order mark
file paper.tex