! LaTeX Error: Missing \begin{document}. is that rare thing among LaTeX errors: a message that lies about itself. Most documents that produce it have a perfectly good \begin{document} in them. Here is the truth — LaTeX never checks whether \begin{document} is present. All it checks is whether a paragraph has started, and it arms a trap in the preamble so that the moment one does, this wording comes out. So the error should be read not as "there is no \begin{document}" but as "something printable turned up before \begin{document}". This page confirms that mechanism in the LaTeX kernel source, then works through the three families of cause: stray body text, a \maketitle left in the preamble, and a single invisible byte.
Why the message lies about itself
LaTeX loads \everypar in the preamble with \@nodocument, and it goes off the instant a paragraph starts. \everypar is TeX’s hook that runs each time a paragraph begins, and a paragraph begins the moment something printable turns up. The relevant lines of latex.ltx, the LaTeX kernel itself, still carry the developers’ own comment.
\gdef\@nodocument{%
\@latex@error{Missing \protect\begin{document}}\@ehd}
\everypar{\@nodocument} %% To get an error if text appears before the
\nullfont %% \begin{document}"To get an error if text appears before the \begin{document}" — the comment says it outright. The job of \begin{document} is to disarm this trap; the trap is not looking for \begin{document} at all. The \nullfont on the next line follows the same thinking: throughout the preamble the current font is one that contains no characters whatsoever, so that anything printed by accident leaves nothing on the page. That settles how to fix it, too: what you are hunting for is not \begin{document} but the printable thing that came before it. Do check once that the line really is there and correctly spelt — rarely but genuinely, a truly absent \begin{document} produces this same message. After that one glance, stop looking at it.
The log hands you the exact character
The l.NN line breaks exactly after the character that caused it. TeX splits the offending line into what it has read and what it has not, and stacks the halves, so the end of the upper half is the culprit itself. That is why it looks as though a word has been chopped in two: below, the T at the start of line 3 was the first printable thing, it started a paragraph, and the trap went off. That document has a perfectly good \begin{document} on line 4.
! LaTeX Error: Missing \begin{document}.
See the LaTeX manual or LaTeX Companion for explanation.
Type H <return> for immediate help.
...
l.3 T
his line is body text by mistake.What counts as printable in a preamble
A preamble may contain only commands that record a value and print nothing. \usepackage, \title, \author, \date, \newcommand and settings of every kind merely store information, so they never start a paragraph. Anything that tries to put something on the page, by contrast, produces this error. The dividing line is not "does it look like body text" but "does it produce output", which is why \title{My Paper} is safe while \maketitle is not — a distinction that looks odd until you see the rule behind it.
- Stray body text — an unfinished sentence left under
\documentclass, or a line meant as a comment whose%was never typed. This is the most common cause by far. - A
\maketitlein the preamble — writing it next to\titleis a frequent slip.\maketitlebelongs after\begin{document}. - Calling a macro that emits text — a bare
\todayin the preamble, or one of your own macros that simply expands to a string. Defining such a macro is safe; calling it is not. - A single stray symbol — a
~right after\usepackage{amsmath}, or a character left outside an unclosed brace. One surplus}, by contrast, gives! Too many }instead.
\documentclass{article}
\title{My Paper} % safe: records a value, prints nothing
\author{Me} % safe
\maketitle % ! LaTeX Error: Missing \begin{document}.
\begin{document}
\maketitle % this is where it belongs
\end{document}When a single invisible byte is to blame
If you have read the preamble three times and found nothing printable, the culprit is very likely a non-breaking space, U+00A0. LaTeX’s utf8enc.dfu contains the line \DeclareUnicodeCharacter{00A0}{\nobreakspace}, which defines U+00A0 as a space that gets printed. It is therefore genuine output, and in a preamble it starts a paragraph. On screen it is indistinguishable from an ordinary space. It arrives entirely naturally: in a \usepackage line copied from a web page or a PDF, from Option+Space on a Mac, from a French keyboard layout. In this case the half-line above l.NN has a distinctive look — there appears to be nothing at the end of it at all.
| Character | How it gets in | What happens in a preamble |
|---|---|---|
U+00A0 | non-breaking space; copied from the web or a PDF, Option+Space on a Mac, French layouts | prints as \nobreakspace, so this error appears — on every engine alike |
U+3000 | ideographic space; typed with a CJK input method still on, or copied from CJK text | this error on xelatex and lualatex; on pdflatex a different one, Unicode character not set up |
U+FEFF | a BOM; some Windows editors add one at the head of a UTF-8 file | on TeX Live 2024, pdflatex, xelatex and lualatex all skip it. Not the cause |
The last row of that table contradicts a widely repeated piece of advice. A BOM is not the cause of this error. On TeX Live 2024 a file with a BOM at its head goes through pdflatex, xelatex and lualatex without so much as a warning — even when the file command reports it as UTF-8 (with BOM) text. That is why "re-save it without the BOM" so often fails to help; the time is better spent hunting for a U+00A0. While we are here: \usepackage[utf8]{inputenc} is no longer needed either, since LaTeX News 28 (2018) made UTF-8 the default input encoding. Encodings in their own right are covered on the "Encoding & newlines" page.
Actually finding the stray byte
A preamble should be pure ASCII, so mechanically searching for lines that contain a non-ASCII character finds it at once. Run it over the whole file, though, and every line of Japanese or Chinese body text matches and the result is useless. The trick is to cut the file at \begin{document} first. Once you have the line, sed -n l shows you its bytes: a U+00A0 prints as \302\240, which settles the identification. Then just retype those two bytes as an ordinary space.
# list preamble lines that contain a non-ASCII character
sed -n '1,/begin{document}/p' paper.tex | LC_ALL=C grep -n '[^ -~]'
# reveal the bytes on a suspect line (U+00A0 shows up as \302\240)
sed -n '3p' paper.tex | sed -n l
# check whether the file carries a byte order mark
file paper.tex