Missing $ inserted

! Missing $ inserted は LaTeX でもっともよく出るエラーで、その下に続く助言は Proceed, with fingers crossed(指を交差させて、幸運を祈りつつ進みます)という一文で終わります。ドナルド・クヌースが 1980 年代に書いた文面が、いまも毎日どこかの画面に出ているわけです。意味はひとつだけ——数式の中でしか通用しないものが、地の文に現れた。犯人はたいてい、そのまま出したかったアンダースコア _、上付きの ^、地の文に紛れた \alpha、あるいはずっと手前で閉じ忘れた $ です。このページでは、メッセージの読み方、報告された行番号が実際の間違いより後ろになる理由、そして地の文・\verb\url という答えの変わる三つの場所で何を書くべきかを扱います。「latex アンダースコア エラー」で検索してたどり着いた人は、まず次の節だけ読めば直ります。

! Missing $ inserted は何に怒っているのか

地の文を組んでいる最中に、数式モード専用のトークンが来た——それだけです。TeX は「数式を開き忘れたのだろう」と推測して $ を勝手に補い、そのまま組版を続けます。だから直し方も一行で言えます。数式として出したいなら $x_1$ のように囲む。文字そのものを出したいなら \_ のようにエスケープする。仕組みを一段掘ると、TeX は入力の一文字ごとに カテゴリコード(catcode) という番号を割り当てていて、_ は 8(下付き)、^ は 7(上付き)、$ は 3(数式シフト)です。そして catcode 8 と 7 は数式モードでしか合法ではありません。\alpha\frac がエラーになるのも同じ理屈で、これらは内部で数式用の文字を要求するからです。

latex
% "$" is not sacred: it is just the character whose catcode is 3.
% Give catcode 3 to "!" and it opens and closes math exactly like $.
\catcode`\!=3
Then !x_1! typesets as math.   % compiles with zero errors

上のコードは冗談のようですが、TeX Live 2024 で実際にエラーなく通ります。$ は聖なる記号ではなく、単に catcode が 3 の文字にすぎない——それが分かると、このエラーの正体が「文字の役割の取り違え」だと腑に落ちます。逆に言えば、地の文で問題を起こす特殊文字は _^ だけではありません。ただし 文字が違えばエラーも違う ので、次の表で当たりをつけてください。ここは検索でよく間違われるところで、& を地の文に書いても ! Missing $ inserted は出ません。

文字catcode地の文に書くと何が起きるか
_8! Missing $ inserted。素の下線が欲しいなら \_
^7! Missing $ inserted。素の記号は \textasciicircum
$3数式の開閉。片方だけだと段落末まで数式が続く
&4別のエラー: ! Misplaced alignment tab character &。素の記号は \&
%14エラーは出ない。行の残りを飲み込むので、閉じ $ を消してしまう
#6! You can’t use ‘macro parameter character #’ in horizontal mode
~13エラーなし。分割禁止の空白になる(素の記号は \textasciitilde

<inserted text>l.3 の読み方

<inserted text> の下に見える $TeX が勝手に足したもので、あなたが書いた文字ではありません。そして l.3 の行は 犯行現場そのものを指しています——TeX は該当行を二段に折って表示し、折れ目が「ここまで読んだ」地点です。次は The value x_1 is here. だけを含む 4 行のファイルを pdfLaTeX(TeX Live 2024)に通した実際の出力です。折れ目が x_ の直後に来ているのが分かります。つまり _ を読んだ瞬間に TeX は止まった。この二段表示は、長い行のどこで詰まったかを一目で教えてくれる、TeX のエラー表示でいちばん便利な部分です。

log
! Missing $ inserted.
<inserted text>
                $
l.3 The value x_
                1 is here.
I've inserted a begin-math/end-math symbol since I think
you left one out. Proceed, with fingers crossed.

同じメッセージは plain TeX でも一字一句そのまま出ます。つまりこれは LaTeX のメッセージではなく TeX エンジン自身のメッセージで、\usepackage を何一つ読み込んでいなくても出ます。原因を探すとき、パッケージを疑う前に自分の書いた文字を疑うべきなのはそのためです。ちなみに助言の最後の一文 Proceed, with fingers crossed は、TeX が 1990 年に凍結されて以来ほぼ変わっていません。

報告された行番号が、間違えた行より後ろになる理由

TeX が報告するのは「気づいた行」であって「間違えた行」ではありません。 _ のように読んだ瞬間に不正なものなら両者は一致しますが、$ の閉じ忘れのように「開いたまま進んでしまった」種類の間違いでは、TeX が異常に気づくのは段落が終わるとき——つまり次の空行です。数式は段落をまたげないという制約があるため、TeX は空行に達して初めて「まだ数式が開いている」と判断します。次のファイルでは間違いは 3 行目にありますが、エラーは 4 行目(空行)で報告されます。

document.tex
\documentclass{article}
\begin{document}
The rate is $x% of the total$ per year.

Next paragraph.
\end{document}
log
! Missing $ inserted.
<inserted text>
                $
l.4

I've inserted a begin-math/end-math symbol since I think
you left one out. Proceed, with fingers crossed.

仕掛けは 3 行目の % です。エスケープし忘れたパーセント記号がその行の残り——閉じの $ を含む——を丸ごとコメントにしてしまい、数式が開いたまま次の行へ進みます。l.4 の右に何も表示されていないのは、4 行目が空行だからです。エラー行が空行のときは、そこより手前で数式が開きっぱなしになっていると読んでください。実務では、報告行の直前の段落を上から見て $ の数を数えるのがいちばん速い方法です。エディタの括弧対応表示は $ を対応させてくれないので、ここは自分で数えるしかありません。同じ理屈で、50\% と書くべきところを 50% と書いた行は、数行あとで正体不明のエラーを生みます。

素のアンダースコアを出す——\_\textunderscore\verb_

data_set.csv のようなファイル名を出したいだけなら、答えは data\_set.csv です。ただし \_\textunderscore の関係は知っておく価値があります。カーネル latex.ltx を読むと \_ は「数式モードなら \nfss@text{\textunderscore}、そうでなければ \textunderscore」と定義されていて、実体は同じものです。つまり地の文では \_\textunderscore は完全に等価で、\_ は数式モードでも使える短縮形にすぎません。もっと重要なのはその先で、既定の OT1 エンコーディングでは下線が文字ではなく罫線として描かれますlatex.ltx の定義は \leavevmode \kern.06em\vbox{\hrule\@width.3em}——つまり幅 0.3em の横罫です。結果として、PDF から本文をコピーしても下線が取れず、PDF 内検索にも引っかかりません。\usepackage[T1]{fontenc} を入れると t1enc.def\DeclareTextSymbol{\textunderscore}{T1}{95} が効いて本物のグリフになり、コピーも検索もできるようになります。実際、同じ文書を pdftotext にかけると OT1 版は data set、T1 版は data_set になります。

書き方結果使いどころ
\__地の文でも数式でも使える標準の書き方
\textunderscore_\_ の実体。地の文では両者に差はない
\verb|a_b|a_bエスケープ不要。中で \_ と書くと \_ がそのまま出る
underscore_パッケージ。読み込むと地の文の素の _ が安全になる
\textasciicircum^素の ^。数式の上付きではなく記号そのもの
latex
% math, when you mean math
The value $x_1$ and $\alpha$ are positive, and $x^2 \ge 0$.

% characters, when you mean characters
Open data\_set.csv, then press \textasciicircum{}C to stop.

% verbatim, when the string is code
Run \verb|make data_set^2| in the shell.

\urlsiunitx の中では答えが逆になる

\url{...} の中では _ をエスケープしてはいけません。 \url{https://example.com/a_b} はそのまま正しく通り、逆に \url{a\_b} と書くと画面に a\_b とバックスラッシュごと出ます。理由は url パッケージが引数を読む前に catcode を組み替え、_ を含む特殊文字を「ふつうの文字」に戻してしまうからです。同じことは siunitx\num\qty にも言えて、これらは地の文にあっても内部で数式モードを開くので、\qty{2.5}{\micro\metre} を本文にそのまま書けます。つまり どの命令の引数の中にいるかで、書くべきものが変わります。困ったときの見分け方は単純で、「その命令が中身を verbatim 的に扱うなら素の文字、そうでないならエスケープ」です。\verb\urllistings\lstinline は前者、\section\caption\footnote は後者です。

latex
\usepackage{url}       % or hyperref, which loads url
\usepackage{siunitx}
...
\url{https://example.com/a_b}    % right: bare underscore
\url{https://example.com/a\_b}   % wrong: prints the backslash
\qty{2.5}{\micro\metre}          % siunitx opens math for you

\[ … \] の中の空行と ! Display math should end with $$

別行立ての数式の中に空行を入れると、一つの空行から三つのエラーが出ます。 式を見やすく分けようとして改行を二つ入れた——それだけで文書が壊れます。実際に \[ E = mc^2F = ma のあいだに空行を置くと、TeX Live 2024 は ! Missing $ inserted! Display math should end with $$! LaTeX Error: Bad math environment delimiter を順に吐きます。数式は段落境界をまたげないという一点から、すべてが派生しています。式を分けたいときは空行ではなく align 環境や \\ を使い、途中に文章を挟みたいなら amsmath\intertext を使ってください。なお ! Display math should end with $$ は、$$ … $ のように開きと閉じの $ の数が食い違ったときにも出ます。LaTeX では $$ ではなく \[ … \] を使うのが定石で、それは $$ が縦方向の空きの設定(\abovedisplayskip など)を無視するからです。

log
! Missing $ inserted.
<inserted text>
                $
l.5

! Display math should end with $$.
<to be read again>
                   \tex_par:D
l.5

! LaTeX Error: Bad math environment delimiter.
l.7 \]

一つの間違いが五つのエラーになるとき

エラーが大量に出たら、直すのは最初の一件だけです。 残りはほぼ確実に、TeX が補った $ が引き起こした二次被害だからです。地の文に \frac{1}{2} と書いただけのファイルを通すと、! Missing $ inserted\frac を読んだ地点)、! Extra }, or forgotten $.\frac の定義に含まれる \over の閉じ括弧)、そしてもう一度 ! Missing $ inserted\end{document} の地点)——三件が連鎖します。TeX は最初のエラーのあと 数式モードに入ったまま 走り続けるので、その後の地の文がすべて数式として解釈され、単語の途中に妙な空きが入り、句読点の間隔が壊れ、追加のエラーが降ってきます。だから直し方は決まっています。最初のエラーを直して、もう一度コンパイルする。二件目以降を先に読み込むのは時間の無駄です。

  • エラーメッセージの l.N へ飛び、その行を折れ目まで読む。折れ目の直後の文字が犯人。
  • 折れ目が空行なら、その手前の段落$ の数を数える(奇数なら閉じ忘れ)。
  • コメント記号のつもりでない % が閉じ $ を飲み込んでいないか、同じ行を最後まで確認する。
  • 数式として出したいなら $ … $、文字として出したいなら \_\%\&\#\textasciicircum{}
  • コードや識別子をそのまま見せたいなら \verblistings。エスケープを一つずつ考えなくて済む。
  • 直したら 必ず一度コンパイルし直してから 次のエラーを読む。