8

With the simple equation environment;

\documentclass{article}
\usepackage{amsmath}
\begin{document}
\begin{equation}
 a \foo b
\end{equation}
\end{document}

you can see a helpful error message, which editors like Kile understands;

! Undefined control sequence.
l.5  a \foo
            b

Making it report 5: Undefined control sequence a \foo

But if you do the same in an amsmath environment, such as the popular align

\documentclass{article}
\usepackage{amsmath}
\begin{document}
\begin{align}
 a \foo b
\end{align}
\end{document}

you get the unhelpful error (and it comes up twice);

! Undefined control sequence.
<argument>  a \foo 
                   b 
l.6 \end{align}

which makes Kile report; 6: Undefined control sequence \end{align}.

Checking the log is tedious and ultimately unrewarding, because not even in there can I find the actual line which contained the undefined control sequence. Can this awful behavior be helped in any way?

  • eqnarray turns out like equation, but there are of plenty or other reasons to not use that. – Mikael Öhman Nov 30 '12 at 18:21
  • 1
    how errors are reported isn't strictly limited to amsmath, but is an underlying design feature of tex. kile isn't parsing the log in the most useful way. – barbara beeton Nov 30 '12 at 18:44
  • 1
    @barbarabeeton In defense of Kile, the TeX log is not written in the most useful way, either. As @MikaelÖhman rightly observes, the line number of the offending \foo is not written out. Now imagine to write a parser that takes all this into account, and is working with all kinds of user defined macros and environments... – mafp Dec 11 '12 at 22:07
  • 2
    @mafp -- i'm not meaning to knock kile; learning to deal with the tex log is a brain-numbing exercise at the best of times. but it's what knuth has given us, so unless tex itself is rewritten to provide more scrutable information, it's what we have to live with. maybe i can find someone to write a guide for tugboat on how to decipher the log more effectively; there isn't anything like that now that i can discover from a quick scan of the cumulative contents. it would be useful. – barbara beeton Dec 11 '12 at 22:45

4 Answers4

14

Simple answer: no. The align environment works very differently from equation: all of the material has to be read before any typesetting. (In that sense, it's similar to the beamer class frame environment, which people also ask similar questions about.) The material to be aligned has to be read before it is typeset to allow measurement, so it's not an option to simply 'drop' this.

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
  • 1
    The fact that Kile chooses to claim that end{align is undefined rather than \foo is I guess down to how it parses the .log file. I doubt much can be done there without re-writing the parser. – Joseph Wright Nov 30 '12 at 16:54
14

The undefined command is, in both cases, the the last token on the line after the error message, which is the documented behaviour here.

Thus the issue is not with TeX or the amsmath package but with the parsing of the log file by Kile which appears to be in error given your description. Perhaps you should raise a bug report with the editor.

Note that the line number has to be that of \end{align} rather than the line that contains the undefined command as the latter is not available. You see the same behaviour if you go

  \def\mycommand{..... \foo .....}

There is no error at that point but if \mycommand is used then you will get an undefined command \foo error message, showing the line number where \mycommand was used not where \foo is used in its definition.

Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036
David Carlisle
  • 757,742
  • The reason the \end{align} line is where \foo is used (rather than the obvious alternative of where it is written in the file) is that align first gathers, then re-typesets its argument, with the actual material presumably being finally placed "on paper" in the course of \end{align}, and not where it first appears in your TeX file. – Ryan Reich Nov 30 '12 at 17:06
  • 1
    @david I regret mentioning Kile as an example. As I mentioned, even looking at the log, without the expected line number its mostly pointless. I'm not suggesting its not following specifications or that it's inconsistent. I'm just saying that it's not at all helpful to me. – Mikael Öhman Nov 30 '12 at 18:05
  • The line number just is not there even internally. Even the file might not be there. When a macro is defined in TeX white space is normalised and there is no record of where the original definition was made. If the definition is dumped in a format the file may be gone altogether. Only when the macro is used will any undefined commands or other errors be found. – David Carlisle Nov 30 '12 at 18:34
  • @David: You're right but the connection with the present question is a little obscure. It's true that in \def\mycommand{\foo}...\mycommand, the error is reported on the latter line, not the one with \def. This does not apparently concern \begin{align}\foo\end{align} because there is no \def evident (\foo is right there in the file) but functionally, it becomes the situation because align collects its contents without expanding them, much as \def. The expansion then occurs in \end{align}. To appearances, though, it's not obvious why this phenomenon is the one that occurs. – Ryan Reich Nov 30 '12 at 19:06
2

TeX is unable to report more information about undefined control sequence. Suppose the following example:

\def\process#1{{\preprocess #1}}
\def\preprocess{\def\foo{Hello}}

\process{ There is a text with \foo. More lines. }

\let\preprocess=\relax

\process{ There is a text with \foo. More lines. }

\bye

The first usage of \process runs without errors, the second usage will cause the error:

! Undefined control sequence.
<argument>  There is a text with \foo 
                                      . More lines. 
\process #1->{\preprocess #1
                            }
l.20 }

You can see, that TeX cannot check, if all control sequences in scanned argument is defined during scanning it and before the argument is used. The meaning of control sequences can be changed before the usage of the argument somewhere in the depths of macros.

Of course, the error message "undefined control sequence" could contain the problematic control sequence printed itself, but such a message format goes against other error message formats reported by TeX. TeX adheres to the principle: All error messages keeps uniform style.

Everything else is a problem of log scanners if they are used.

wipet
  • 74,238
0

The reason of the error, as explained in other answers, is that align is not a "real" environment, rather it takes the environment body as an ⟨argument⟩ and thus lose the line number information as explained in "Line number of macro is not shown" section in my answer to "What does \errorcontextlines do?" question.

If it's only for debugging purpose, you can use a little hack to reimplement align environments etc. in a way that does not measure its content:

%! TEX program = lualatex
\documentclass{article}
\usepackage{amsmath}
%\usepackage{lua-visual-debug}
\begin{document}

\ExplSyntaxOn

%\iffalse

\cs_if_exist:NF \RenewEnvironmentCopy { % https://tex.stackexchange.com/a/680717/250119 and https://tex.stackexchange.com/a/680721/250119 \NewDocumentCommand \RenewEnvironmentCopy {mm} { \expandafter \RenewCommandCopy \csname#1\expandafter\endcsname \csname#2\endcsname \expandafter \RenewCommandCopy \csname end#1\expandafter\endcsname \csname end#2\endcsname } }

% this can also be implemented with tabular and https://tex.stackexchange.com/q/112576/250119 \RenewDocumentEnvironment{split}{}{ \let \ \cr \vbox\bgroup \halign\bgroup & \hfill $\displaystyle ##$ & $\displaystyle {}##$ \hfill \cr }{ \crcr \egroup \egroup }

\RenewDocumentEnvironment{multline}{}{ \par

\lineskip=3pt \leftskip=0pt \rightskip=0pt plus 1fil \parindent=0pt \parfillskip=0pt \def \ { $ \par \leftskip=0pt plus 1fil $\displaystyle {} } $\displaystyle {} }{ $ \rightskip=0pt \par \endcenter }

\RenewDocumentEnvironment{gather*}{}{ \par

\lineskip=3pt \leftskip=0pt plus 1fil \rightskip=0pt plus 1fil \parindent=0pt \parfillskip=0pt \def \ { $ \par $\displaystyle } $\displaystyle }{ $ \par } \RenewEnvironmentCopy{gather}{gather*}

\RenewDocumentEnvironment{align}{}{ \lineskip=3pt $$ \vbox\bgroup \let \ \cr \tabskip=0pt plus 1fil \halign to \linewidth\bgroup & \tabskip=0pt \hfil $\displaystyle ##$ & $\displaystyle {}##$ \hfil \tabskip=0pt plus 1fil \cr }{ \crcr \egroup \egroup$$ } \RenewEnvironmentCopy{align}{align}

\RenewDocumentEnvironment{flalign}{}{ $$ \vbox\bgroup \let \ \cr \tabskip=0pt \halign to \linewidth\bgroup & \tabskip=0pt \hfil $\displaystyle ##$ & $\displaystyle {}##$ \hfil \tabskip=0pt plus 1fil \cr }{ \crcr \egroup \egroup$$ } \RenewEnvironmentCopy{flalign}{flalign}

\use_none:n \fi \ExplSyntaxOff

\begin{equation} a=b \end{equation} \begin{equation} a=b \end{equation} \begin{equation}\label{xx} \begin{split} a& =b+c-d\ & \quad +e-f\ & =g+h\ & =i \end{split} \end{equation} \begin{multline} a+b+c+d+e+f\ +i+j+k+l+m+n \end{multline} \begin{multline} a+b+c+d+e+f\ a+b+c+d+e+f\ +a+b+c+d+e+f\ +i+j+k+l+m+n \end{multline} \begin{gather} a_1=b_1+c_1\ a_2=b_2+c_2-d_2+e_2 \end{gather} \begin{align} a_1& =\frac{1}{2} b_1+c_1\ a_2& =b_2+c_2-d_2+e_2 \end{align} \begin{itemize} \item The formula is \begin{align} a_1& =\frac{1}{2} b_1+c_1\ a_2& =b_2+c_2-d_2+e_2 \end{align} some more text. \end{itemize} \begin{align} a_{11}& =b_{11}& a_{12}& =b_{12}\ a_{21}& =b_{21}& a_{22}& =b_{22}+c_{22} \end{align} \begin{flalign} a_{11} + b_{11}& = c_{11}& a_{12}& =b_{12}\ b_{21}& = c_{21}& a_{22}& =b_{22}+c_{22} \end{flalign}

\end{document}

Of course, this implementation output quality is nowhere comparable to, but it preserves line number in error messages and also synctex information. For draft compilations/debugging, I think it may be useful.

Note: this is not very well-tested. It may create other errors that the original environment does not.

For a comparison of output quality (it's supposed to be bad! Do not use in final document):

output

Left is normal output, right is output with the hack. (you can also notice tag numbers are missing)


Given the complicated things that amsmath do with its content, I think doing multiple passes for measurement is required.

Detail of what amsmath do:

amsmath source documentation snippet

However I can think of 2 workarounds:


Using that idea, at the moment another hack is possible with my cprotectinside package:

%! TEX program = lualatex
\documentclass{article}
\usepackage{amsmath}
\usepackage{cprotectinside}
\cprotectinsideEnableSyncInner
\begin{document}

\errorcontextlines=10

\cprotectinside{|}{ \begin{align} |% a_{11}& =b_{11}& a_{12}& =b_{12}\ a_{21}& =b_{21}& a_{22}& =b_{22}+c_{22} | \end{align} }

\end{document}

(it also preserves synctex if lualatex is used, and error messages at least point to the correct lines, although filename may be a bit confusing. Note that it requires a new version on GitHub that is not published on CTAN)

user202729
  • 7,143