[Tex/LaTex] “Runaway argument?” error with \NewEnviron

environmentsinputprogramming

I have two files. Basically, the main file just loads the environ package and inputs the secondary file. The secondary file creates an environment that basically does nothing, and creates one instance of that environment:

main.tex:

\documentclass{scrartcl}
\usepackage{environ}
\begin{document}
  \input{secondary.tex}
\end{document}

secondary.tex:

\NewEnviron{myenv}{\BODY}
\begingroup
  \myenv
    Hello
  \endmyenv
\endgroup

I get the following error message:

Runaway argument?
Hello \endmyenv \endgroup \par
! File ended while scanning use of \document.
<inserted text>
                \par
l.5 \input{secondary.tex}

The example works as expected when I make any (!) of the following changes:

  • Use \newenvironment instead of \NewEnviron
  • Use \begin{myenv}...\end{myenv} instead of \begingroup\myenv...\endmyenv\endgroup
  • Place the contents of secondary.tex directly in main.tex instead of inputting secondary.tex

Why does this not work?

Best Answer

environ creates an environment that uses TeX parameter text in definitions to capture the environment contents. This parameter-text style definition allows the package to capture the environment contents, but it also requires a very strict usage of the macros/definitions... something you're not adhering to when using a command-style environment (\myenv...\endmyenv rather than \begin{myenv}...\end{myenv}).

Broadly speaking, \NewEnviron{myenv} creates a macro \myenv which designates the start of the environment, and this macro is on the lookout for exactly \end{myenv}... nothing else.

The example fails when using \input since there's no explicit \end{<something>} that \myenv is searching for. It reaches the end of secondary.tex before it ever finds any resemblance of \end. It's actually looking for \end{document} (see below).

Let's look at the three instances you mention that corrected the problem and see why that's the case:

  • Use \newenvironment instead of \NewEnviron

    It should be obvious why this works. \newenvironment{myenv} defines both \myenv and \endmyenv, so using the command-definition of the environment should work without problem;

  • Use \begin{myenv}...\end{myenv} instead of \begingroup\myenv...\endmyenv\endgroup

    This is exactly what environ expects you should use when you define an environment using \NewEnviron. So, this works out-of-the-box, since you've explicitly used the environment termination \end{myenv};

  • Place the contents of secondary.tex directly in main.tex instead of inputting secondary.tex

    Since you're using the command-form of an environment, the "current environment" (known to LaTeX as \@currenvir) never really changes. That is, it remains as being inside the document environment (due to a call \begin{document}). So, your end-of-environment capture is actually \end{document} according to newenviron's setup. Your environment "starts" with \myenv and ends with \end{document}. Weird, but that's how it works when you don't use the expected environment scoping \begin{...}...\end{...}.

For it to work properly within the context of newenviron, use the scoping \begin{myenv}...\end{myenv}. This scoping provides the necessary grouping you implement through \begingroup...\endgroup.

Alternatively, define \myenv to capture stuff until it reaches \endmyenv:

\documentclass{scrartcl}
\usepackage{filecontents}
\begin{filecontents*}{secondary.tex}
\long\def\myenv #1\endmyenv{#1}% Fake myenv environment that captures its body
\begingroup
  \myenv
    Hello
  \endmyenv
\endgroup
\end{filecontents*}
\begin{document}
\input{secondary.tex}
\end{document}
Related Question