Undefined control sequence

! Undefined control sequence 是几乎每个人遇到的第一个 LaTeX 错误,而严格来说它根本不是 LaTeX 的错误:不加载任何 LaTeX 的纯 TeX 也会原样打印这三个词。control sequence(控制序列)是 TeX 自己的术语,指「反斜杠后面跟一个名字」,这条消息只是在说「这个名字不在字典里」。它之所以是搜索量最大的一条错误,关键在于之后发生的事——TeX 不会停下。它会悄悄丢掉这个不认识的命令,继续往下读,最后交给你一份 PDF,而命令的参数已经变成了正文文字。本页讲清占绝大多数情况的三种原因——拼写错误、没有加载的宏包、在定义之前就使用的宏——以及一个陷阱:日志里的行号是 TeX 察觉 到问题的地方,未必是你写错的地方。

如何读这条错误:折行的位置就是元凶

折成两行之后,上面那行末尾的命令就是未定义的命令。 TeX 报告错误时,会把出错的那一行拆成「已经读过的部分」和「还没读的部分」,上下排列。它停止阅读的瞬间正是出问题的瞬间,因此这个断点直接指向元凶。下面是把 \textbf 误打成 \textbnf 时的真实日志。

terminal
! Undefined control sequence.
l.4 This is \textbnf
                    {bold} text.

l.4 表示第 4 行。如果问题发生在数学模式里,l.4 上方还可能多出 <recently read> \fra 这样一行,单独把命令名列出来。无论哪种情况,第一步都是在自己的源文件里搜索这个命令名。如果拼写没错,就继续看下一节。另外,如果打错的是宏包的名字,报的就不是这条错误,而是 ! LaTeX Error: File 加上找不到的那个 .sty 文件名。

原因只有三种:拼写、宏包、定义的先后

实际使用中,未定义命令的原因基本只有这三种:拼写错误、忘记加载宏包、在定义之前就使用了宏。 顺序也有讲究——按这个次序依次排查最快,因为核对拼写只要几秒,核对宏包只需扫一眼导言区,只有宏的情况才真正花时间。

  • 拼写错误——把 \frac 写成 \fra、把 \textbf 写成 \textbnf、把 \begin 写成 \begni。命令名区分大小写,所以把 \LaTeX 写成 \Latex 同样是未定义。
  • 没有加载宏包——命令拼写没错,但定义它的宏包没有在导言区用 \usepackage 加载。参见下一节的对照表。
  • 在定义之前使用了宏——忘了写 \newcommand、在定义那一行之前就用了它,或者定义被写在花括号或环境里,出了作用域就消失了。

这个命令属于哪个宏包?

下表列出最常因这个原因而未定义的命令,以及定义它们的宏包(已在 TeX Live 2024 上用 \ifdefined 实测)。最常见的混淆是 amsmathamssymb \lVertamsmath 里而不在 amssymb 里;\mathbb\thereforeamssymb 里而不在 amsmath 里。如果数学命令「明明加载了宏包」却仍然未定义,先怀疑这一点。表中没有的命令,可以用 texdoc PACKAGE 打开该宏包的说明书查证。

命令宏包备注
\includegraphicsgraphicx插入图片;未加载时选项会被当成正文印出来
\toprulebooktabs\midrule\bottomrule 同理
\lVertamsmathamssymb没有\rVert 同理
\mathbbamssymb只加载 amsmath 不够(实际来自 amsfonts
\thereforeamssymb\because 同理;amsmath 里没有
\bmbm数学粗体;\boldsymbol 属于 amsmath
\coloneqqmathtools只加载 amsmath 不够
\multirowmultirow表格单元格的纵向合并
\FloatBarrierplaceins阻止浮动体越过此处
\hrefhyperref若只需要 \url,加载 url 即可
\textcolorxcolor\definecolor\colorbox 同理
\SIsiunitx新写法是 \qty;同一个宏包

\newcommand 没生效:顺序与作用域

TeX 从上到下只读一遍,所以定义必须在使用之前。 人看源文件看的是「整体」,而 TeX 是逐行往下读,直到走到 \newcommand 那一刻才把这个名字登记进字典。因此在第 200 行定义、在第 40 行使用,就是未定义。另一个坑是作用域:写在花括号或环境内部的宏,到闭合括号处就消失了。只要把定义统一放进导言区,这两种事故都不会发生。\newcommand 本身的写法(参数、默认值、与 \renewcommand 的区别)由「定义宏」那一页负责。

latex
\documentclass{article}
\begin{document}
% too early: \R is not in the dictionary yet
$\R$
\newcommand{\R}{\mathbb{R}}

% scoped: \tmp dies at the closing brace
{\newcommand{\tmp}{scoped}\tmp}
\tmp
\end{document}

报告的行号未必是出错的地方

如果 l.NN 上方出现含有 -> 的行,错误就在那一行所指名的宏内部。 l.NN 只是 TeX 察觉到异常的地方——也就是使用这个宏的那一行。定义本身可能在几百行之外,也可能藏在某个宏包里。下例中 \mysq 调用了 \mynorm,而 \mynorm 用到 \lVert,却忘了加载 amsmath。日志里那句 \mynorm #1->\lVert 是唯一的线索。

terminal
! Undefined control sequence.
\mynorm #1->\lVert
                   #1 \rVert
l.5 The value $\mysq{x}
                       $ is here.

注意这段日志里根本没有出现 \mysq。因为 LaTeX 把 \errorcontextlines 设成了 -1(见 latex.ltx 第 535 行),所以宏调用链只显示最内层的一级。当故障发生在宏包内部时,这就不够用了。在导言区写上 \errorcontextlines=999 再编译一次,就会多出 \mysq #1->\mynorm {#1} 一行,整条调用路径一览无余。查清原因后记得删掉——日常还是安静的日志更好读。

PDF 照样生成——这才是真正的危险

未定义命令不会中断排版。TeX 只把这一个命令丢掉,剩下的部分照普通文字排。 也就是说,忘了加载 graphicx 却写下 \includegraphics[width=3cm]{example-image},结果不只是图片没了,而是生成一份把选项和文件名当作正文印出来的 PDF。编辑器和构建工具默认带 -interaction=nonstopmode,遇错也继续跑,于是就有了「一路无视警告、把生成出来的 PDF 交上去」的事故。只要日志里还留着哪怕一个未定义命令,就不能相信那份 PDF。

latex
% graphicx was never loaded
\includegraphics[width=3cm]{example-image}

% the run still succeeds, and this is what lands on the page:
%   [width=3cm]example-image