Fix “Invalid Escape Sequence Near \%” in LuaLaTeX Chapter Titles
At a glance
- 1. Identify the parser
The message is from Lua: a backslash-percent sequence is not a valid escape in a Lua string literal.
- 2. Build the TeX command safely
Use string.char(92,37) to produce the characters backslash and percent without writing an escape sequence.
- 3. Wrap it robustly
Define a robust LaTeX macro outside the chapter command, then use that macro in the title.
- 4. Handle PDF bookmarks
If hyperref is loaded, give the title a plain-text alternative for the PDF bookmark.
The error invalid escape sequence near "\%" comes from Lua parsing a string, not from LaTeX interpreting a percent sign. Lua does not recognize \% as a valid string escape. The complication is that the text is being passed through both TeX and Lua, and a \ that looks right for Lua can be changed or interpreted while TeX reads the input.
A reliable fix is to avoid spelling the backslash and percent sign in the Lua string. Generate those characters by their character codes, and place the \directlua call in a robust LaTeX command before using it in a chapter title.
Why the error occurs
Lua uses backslashes to introduce escapes inside quoted strings. For example, \n represents a newline and \\ represents a literal backslash. But \% is not a Lua escape, so a string containing it causes a syntax error.
TeX adds another layer: it tokenizes the input before Lua receives it. In particular, a percent sign normally starts a TeX comment, while \% is TeX’s command for printing a percent sign. Trying to make the same characters serve both parsers often leads to the invalid escape.
There is also a LaTeX-specific issue. A \chapter title is a moving argument: LaTeX may write it to the table of contents and use it in running marks. Code that works in ordinary paragraph text can misbehave when expanded or written from a title.
A safe, complete example
Compile this example with LuaLaTeX. It defines a robust command that asks Lua to emit the TeX command \%:
\documentclass{book}
\DeclareRobustCommand{\LuaPercent}{%
\directlua{tex.sprint(string.char(92,37))}%
}
\begin{document}
\chapter{A value of 10\LuaPercent}
The value is 10\LuaPercent.
\end{document}
The Lua expression string.char(92,37) creates a string consisting of character 92 (backslash) followed by character 37 (percent sign). tex.sprint sends that string back to TeX as input, where \% prints a percent sign. The chapter title therefore displays as “A value of 10%,” and the robust macro can be written to and read from the table-of-contents file.
Defining the command outside \chapter also keeps the Lua code out of the title’s moving-argument machinery. If you only need a percent sign, the simpler solution is to type \% directly in the chapter title; Lua is unnecessary for that.
If you use hyperref
PDF bookmarks need plain text, not arbitrary TeX or Lua commands. With hyperref, give the title a bookmark-safe alternative using \texorpdfstring:
\usepackage{hyperref}
% ...define \LuaPercent as above...
\chapter{A value of 10\texorpdfstring{\LuaPercent}{\%}}
The first argument is used in the typeset title; the second supplies the bookmark text. If the chapter title does not need Lua-specific output, using \% directly is usually simpler and avoids this extra handling.
Common mistakes
- Writing
"\%"in Lua code: Lua rejects the escape. The fact that\%is meaningful to TeX does not make it a valid Lua string escape. - Writing a raw
%in\directlua: TeX may treat it as the start of a comment before Lua sees it. - Doubling backslashes without checking TeX tokenization: Escaping for Lua alone may not produce the intended Lua source after TeX reads the argument.
- Putting a direct Lua call directly in a chapter title: Chapter titles are written to auxiliary files and may also be converted to PDF bookmarks. A robust command, and a bookmark alternative when needed, handles those contexts more safely.
References
- The LuaTeX Reference Manual
- LaTeX2e unofficial reference manual: \DeclareRobustCommand
Spotted an error? Send a correction. Read how we write and check articles in our editorial policy.