! Undefined control sequence 是几乎每个人遇到的第一个 LaTeX 错误,而严格来说它根本不是 LaTeX 的错误:不加载任何 LaTeX 的纯 TeX 也会原样打印这三个词。control sequence(控制序列)是 TeX 自己的术语,指「反斜杠后面跟一个名字」,这条消息只是在说「这个名字不在字典里」。它之所以是搜索量最大的一条错误,关键在于之后发生的事——TeX 不会停下。它会悄悄丢掉这个不认识的命令,继续往下读,最后交给你一份 PDF,而命令的参数已经变成了正文文字。本页讲清占绝大多数情况的三种原因——拼写错误、没有加载的宏包、在定义之前就使用的宏——以及一个陷阱:日志里的行号是 TeX 察觉 到问题的地方,未必是你写错的地方。
如何读这条错误:折行的位置就是元凶
折成两行之后,上面那行末尾的命令就是未定义的命令。 TeX 报告错误时,会把出错的那一行拆成「已经读过的部分」和「还没读的部分」,上下排列。它停止阅读的瞬间正是出问题的瞬间,因此这个断点直接指向元凶。下面是把 \textbf 误打成 \textbnf 时的真实日志。
! 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 实测)。最常见的混淆是 amsmath 与 amssymb。 \lVert 在 amsmath 里而不在 amssymb 里;\mathbb 和 \therefore 在 amssymb 里而不在 amsmath 里。如果数学命令「明明加载了宏包」却仍然未定义,先怀疑这一点。表中没有的命令,可以用 texdoc PACKAGE 打开该宏包的说明书查证。
| 命令 | 宏包 | 备注 |
|---|---|---|
\includegraphics | graphicx | 插入图片;未加载时选项会被当成正文印出来 |
\toprule | booktabs | \midrule 与 \bottomrule 同理 |
\lVert | amsmath | amssymb 里没有;\rVert 同理 |
\mathbb | amssymb | 只加载 amsmath 不够(实际来自 amsfonts) |
\therefore | amssymb | \because 同理;amsmath 里没有 |
\bm | bm | 数学粗体;\boldsymbol 属于 amsmath |
\coloneqq | mathtools | 只加载 amsmath 不够 |
\multirow | multirow | 表格单元格的纵向合并 |
\FloatBarrier | placeins | 阻止浮动体越过此处 |
\href | hyperref | 若只需要 \url,加载 url 即可 |
\textcolor | xcolor | \definecolor 与 \colorbox 同理 |
\SI | siunitx | 新写法是 \qty;同一个宏包 |
\newcommand 没生效:顺序与作用域
TeX 从上到下只读一遍,所以定义必须在使用之前。 人看源文件看的是「整体」,而 TeX 是逐行往下读,直到走到 \newcommand 那一刻才把这个名字登记进字典。因此在第 200 行定义、在第 40 行使用,就是未定义。另一个坑是作用域:写在花括号或环境内部的宏,到闭合括号处就消失了。只要把定义统一放进导言区,这两种事故都不会发生。\newcommand 本身的写法(参数、默认值、与 \renewcommand 的区别)由「定义宏」那一页负责。
\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 是唯一的线索。
! 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。
% 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