re.error: nothing to repeat (tested)
re.error: nothing to repeat at position N is Python's regex engine telling you a repeat operator (*, +, ?, or {m,n}) has nothing in front of it to repeat — usually a stray character at the start of a pattern or inside a group, not a Python bug. We reproduced it, its sibling multiple repeat, and three other common re.compile() failures, with the exact position each one reports.
1. The exact error: a repeat operator with nothing before it
We compiled a pattern that's just a bare repeat operator:
>>> import re
>>> re.compile("*")
Traceback (most recent call last):
...
re.PatternError: nothing to repeat at position 0
On Python 3.13.5, the exception class is re.PatternError; we confirmed re.error is re.PatternError returns True, so except re.error: still catches it — PatternError is just the new, clearer name for the same class. + and ? alone produce the identical message and position. The regex engine reads * as "repeat the previous token zero or more times," and position 0 has no previous token at all.
2. Inside a group: (*)
The same rule applies inside parentheses. We compiled "(*)":
>>> re.compile("(*)")
re.PatternError: nothing to repeat at position 1
Position 1 is the * itself, right after the group's opening ( — there's nothing between them for * to repeat. This is the version of the error people usually hit by accident: a pattern built from string concatenation or an f-string where an expected literal character ended up empty, leaving a bare operator exposed.
3. The sibling error: multiple repeat
Stacking two repeat operators on the same token is a different message, not the same one:
>>> re.compile("a**")
re.PatternError: multiple repeat at position 2
a* already means "zero or more a's"; a second * right after has nothing new to apply to (the first * already consumed the repeat slot), so Python reports multiple repeat at the position of the second operator, not nothing to repeat. If you meant "zero or more, non-greedy," the fix is a*?, not a**.
4. Three other re.compile() errors worth telling apart
We also triggered three more common patterns so you can match the message to the real cause instead of guessing:
| Pattern | Exact message | Real cause |
|---|---|---|
a{2,1} | min repeat greater than max repeat at position 2 | Swapped bounds in {m,n} — min must be ≤ max |
[z-a] | bad character range z-a at position 1 | Character class range written backwards (z comes after a) |
a(b | missing ), unterminated subpattern at position 1 | Unclosed ( — count your parens, not just your quotes |
a)b | unbalanced parenthesis at position 1 | A ) with no matching ( before it |
(?P<n>a)(?P<n>b) | redefinition of group name 'n' as group 2; was group 1 at position 12 | Two named groups reuse the same name |
Every one of these is raised at compile time, before the pattern ever runs against a string — so the traceback points at your re.compile() or re.match() call, not at the data you were trying to match.
5. Reading the position number
The e.pos attribute on a caught exception gives you the same 0-indexed offset the message prints, and e.msg gives you just the text without the position suffix:
>>> try:
... re.compile("*")
... except re.error as e:
... print(e.pos, e.msg)
...
0 nothing to repeat
For a pattern assembled dynamically (from a loop, a config file, or user input), slicing the original pattern string at e.pos is faster than eyeballing a long generated regex to find the broken character.
6. Fix checklist
- Bare
*/+/?at the start of a pattern or right after(,|, or another repeat operator — usually a missing literal character before it, check string concatenation and f-string substitutions. - Two repeat operators back to back (
a**,a*+) — pick one; use*?for non-greedy, not**. - If you want to match a literal
*,+, or?character, escape it:\*,\+,\?. - Count parentheses, not quotes, when you get an unterminated-subpattern or unbalanced-parenthesis error — the message position points at the first one Python couldn't match.
- For
{m,n}errors, confirmm <= n.
Related: for a pattern that compiles but doesn't match what you expect at runtime, that's a logic issue, not a re.error — add re.VERBOSE and comments to the pattern to debug it step by step.
Frequently asked questions
What does 'nothing to repeat' mean in a Python regex?
It means a repeat operator (*, +, ?, or {m,n}) appears where there's no preceding token for it to repeat - usually at the very start of the pattern, right after an opening parenthesis, or right after another repeat operator. We measured this at position 0 for a bare *, +, or ? pattern on Python 3.13.5.
Is re.error the same as re.PatternError?
Yes, on Python 3.13.5 we confirmed re.error is re.PatternError returns True. PatternError is the newer, more descriptive name; re.error is kept as an alias so existing except re.error: code keeps working.
Why do I get 'multiple repeat' instead of 'nothing to repeat' for a**?
Because a* is already valid (zero-or-more a), so the second * isn't attached to an empty slot - it's attached to an already-repeated token, which Python flags as multiple repeat specifically, at the position of the second operator.
How do I match a literal asterisk or plus sign in a regex?
Escape it with a backslash: \\* matches a literal *, \\+ matches a literal +, \\? matches a literal ?. Without the backslash, Python's re module always treats these as repeat operators, never as literal characters.
Does this error happen at compile time or when the regex runs against text?
At compile time, inside re.compile() (or any re function that compiles a pattern internally, like re.match() or re.sub()). We confirmed all five patterns in this guide raise immediately, before any string is matched against them.
Written with AI assistance. Measured on 2026-10-03 with Python 3.13.5 on Linux, calling re.compile() directly on each pattern in a python3 -c one-liner. Every message and position below is copy-pasted output, not paraphrased. PlainHeaders is our extension; Python is free.