IndentationError and TabError (tested)

IndentationError means a line's leading whitespace doesn't match what Python expected at that point — too much, too little, or ambiguous. There are three distinct IndentationError messages plus a separate TabError subclass for mixed tabs and spaces. We reproduced all four on a live interpreter so the messages below are exact, not paraphrased.

1. Four distinct errors, same root cause

All four come from inconsistent leading whitespace, but Python reports them differently depending on exactly what went wrong:

SituationExact error (Python 3.13.5)
A line indented with no block expecting itIndentationError: unexpected indent
A : block (if/for/def/...) with nothing indented under itIndentationError: expected an indented block after 'if' statement on line 1
A dedent that doesn't land on any open indentation levelIndentationError: unindent does not match any outer indentation level
Tabs and spaces mixed so Python can't tell the indent depthTabError: inconsistent use of tabs and spaces in indentation

TabError is a subclass of IndentationError, but it's a separate exception name with its own message — code that catches IndentationError also catches TabError, but the printed error name is different, so searching for the exact string matters.

2. Cause 1: unexpected indent

This fires when a line is indented more than the line before it, but nothing (no : block) called for a new indentation level.

x = 1
    y = 2
  File "case1.py", line 2
    y = 2
IndentationError: unexpected indent

Fix: remove the extra leading whitespace so the line lines up with the statement above it. This is the most common case — usually a stray space or tab left over from pasting code, or an editor auto-indenting after a colon that was later deleted.

3. Cause 2: expected an indented block

Any line ending in : (if, for, while, def, class, try, with...) requires at least one indented statement under it — even pass counts, but nothing is not allowed.

if True:
pass
  File "case2.py", line 2
    pass
    ^^^^
IndentationError: expected an indented block after 'if' statement on line 1

Python 3.10+ names the specific statement and line number in the message (after 'if' statement on line 1); older Python 3 versions just say IndentationError: expected an indented block with no statement name. Fix: indent the line(s) under the colon, or add a placeholder pass if the block is intentionally empty (e.g. a stub you haven't written yet).

4. Cause 3: unindent does not match any outer indentation level

This fires when a line dedents to a depth that isn't any of the currently-open indentation levels — not "less indented", but indented to a width nothing else used. It's commonly worded as searching for "unexpected unindent", but the actual Python 3.13.5 message is the one below.

if True:
  x = 1
  if True:
    y = 2
   z = 3
  File "case3.py", line 5
    z = 3
         ^
IndentationError: unindent does not match any outer indentation level

Here the outer block is at 2 spaces and the inner block is at 4 spaces; z = 3 at 3 spaces matches neither. Fix: pick one consistent indent width (4 spaces is the PEP 8 standard) and make every dedent line up exactly with an enclosing line's indentation — don't eyeball it.

5. Cause 4: TabError (mixed tabs and spaces)

This is a different exception class from the three above. It fires when a file mixes literal tab characters and space characters in a way Python can't unambiguously resolve into one indentation depth.

if True:
	x = 1
        y = 2

Run cat -A to see the raw whitespace (^I is a literal tab, $ marks line end):

if True:$
^Ix = 1$
        y = 2$
  File "case4.py", line 3
    y = 2
TabError: inconsistent use of tabs and spaces in indentation

Fix: never mix tabs and spaces in the same file. Configure your editor to insert spaces when Tab is pressed, and convert existing tabs (most editors have a "convert indentation to spaces" command).

6. Fix checklist

Frequently asked questions

What's the difference between IndentationError and TabError?

TabError is a subclass of IndentationError, but it's reported under its own name. IndentationError covers wrong indent depth (too much, too little, or a bad dedent) when whitespace is consistent; TabError specifically means tabs and spaces are mixed in a way Python can't resolve into one depth. We measured both: 'IndentationError: unexpected indent' versus 'TabError: inconsistent use of tabs and spaces in indentation'.

Why does Python say 'expected an indented block after if statement on line 1'?

Because Python 3.10+ names the specific statement and its line number in the message. A colon-terminated line (if/for/while/def/class/try/with) requires at least one indented statement under it; we measured this exact wording on Python 3.13.5. On older Python 3 versions the message is shorter: just 'IndentationError: expected an indented block', without naming the statement.

What does 'unindent does not match any outer indentation level' mean?

It means a line dedented to a whitespace width that doesn't equal any currently-open indentation level - not simply 'less than before', but a width nothing else in the enclosing blocks used. We reproduced this by mixing 2-space and 4-space indents: a line at 3 spaces matched neither the 2-space outer block nor the 4-space inner block.

How do I find mixed tabs and spaces before running the file?

Run python3 -m tabnanny yourfile.py - it prints the exact file and line of the first ambiguous indentation with no traceback. cat -A yourfile.py also shows raw whitespace directly, with tabs rendered as ^I, so mixed indentation is visible at a glance.

Does an empty if block always raise IndentationError?

Yes, any block header ending in a colon (if, for, while, def, class, try, with) must have at least one indented statement beneath it. If the block is intentionally empty, use pass as a placeholder statement - we measured that pass alone is enough to satisfy Python, with no other content required.

Written with AI assistance. Measured on 2026-10-03 with Python 3.13.5 on Linux using plain python3 one-liners. Every traceback below is copy-pasted output, not paraphrased. PlainHeaders is our extension; Python is free.