What is an IndentationError in Python?
An IndentationError is a syntax error that Python raises when the interpreter encounters incorrect or inconsistent use of whitespace at the beginning of code lines. Unlike many programming languages that use braces {} or keywords like begin/end to define code blocks, Python relies entirely on indentation to determine the structure and grouping of statements. When indentation is missing, mismatched, or mixes tabs with spaces in an incompatible way, Python halts execution and throws this error.
The error typically appears in one of two forms:
- IndentationError: expected an indented block – triggered when a statement that introduces a new block (such as
if,for,def, orclass) is not followed by any indented code. - IndentationError: unindent does not match any outer indentation level – raised when the amount of whitespace used to indent a line does not align with any previous block level.
- TabError: inconsistent use of tabs and spaces in indentation – a subclass of
IndentationErrorthat specifically flags a mixture of tab characters and spaces.
Below is a minimal example that triggers an IndentationError:
def greet():
print("Hello, world!") # Missing indentation inside the function body
Running this code produces:
IndentationError: expected an indented block
Why Indentation Matters So Much in Python
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Indentation is not merely a cosmetic convention in Python — it is part of the language syntax. The Python interpreter uses indentation levels to build the Abstract Syntax Tree (AST) that represents the logical structure of your program. This design choice, often called the "off-side rule," forces code to be visually consistent with its logical structure. The benefits are substantial:
- Readability: Every Python program looks structurally similar, making code easier to read and review across teams and projects.
- Reduced ambiguity: There is no mismatch between what you see and what the compiler parses. Braces can sometimes obscure logical errors that are immediately visible with indentation-based block delimitation.
- Less syntactic noise: Removing braces and semicolons lets developers focus on the logic itself rather than on delimiters.
- Consistency enforcement: The interpreter actively prevents sloppy formatting that could hide bugs in other languages.
However, this strictness means that even a single space out of place can break a program. Understanding exactly how Python resolves indentation is critical for debugging and writing robust code.
How Python Parses Indentation
Before diving into fixes, it helps to understand the mechanics. Python's tokenizer converts source code into a stream of tokens. Alongside the usual tokens like NAME, STRING, and OPERATOR, it emits special tokens: INDENT, DEDENT, and NEWLINE. The parser maintains an internal stack of indentation levels. When a line begins with more whitespace than the current level, an INDENT token is pushed onto the stack. When a line has less whitespace, one or more DEDENT tokens are emitted to pop levels off the stack until a match is found. If no matching level exists, the dreaded unindent does not match any outer indentation level error appears.
Python treats a tab character as equivalent to 8 spaces for indentation comparison, but only if you are consistently using tabs. When tabs and spaces are mixed, Python 3 strictly refuses to guess and raises a TabError instead.
Common Causes and How to Fix Them
1. Missing Indentation Under a Block Starter
This is the most frequent cause for newcomers. After a line ending with a colon — such as if, elif, else, for, while, def, class, try, except, finally, with, or match/case — Python expects the next non-empty line to be indented.
Broken code:
temperature = 35
if temperature > 30:
print("It's hot outside!") # No indentation
Fixed code:
temperature = 35
if temperature > 30:
print("It's hot outside!") # 4 spaces (or one tab) added
2. Inconsistent Indentation Within the Same Block
All lines that belong to the same logical block must share exactly the same leading whitespace. Even an extra space or a missing space on one line will break the block.
Broken code:
def calculate_total(items):
total = 0
for item in items:
total += item["price"]
return total # Misaligned with the 'for' block
Here, return total has 6 spaces while total += item["price"] has 8 spaces (assuming 4-space base indentation). Python cannot determine which block return belongs to.
Fixed code:
def calculate_total(items):
total = 0
for item in items:
total += item["price"]
return total # Now aligned with the 'for' block (4 spaces from def)
Use your editor's "show whitespace" or "show invisibles" feature to spot inconsistencies. Most modern editors also display vertical guidelines for indentation columns.
3. Mixing Tabs and Spaces
This is a notorious source of subtle IndentationError and TabError problems. You might copy-paste code from a source that uses tabs into a file configured for spaces, or accidentally press the Tab key in a space-indented file.
Broken code:
def process_data(data):
results = []
\tfor entry in data: # Tab-indented, but the previous line used spaces
\t results.append(entry * 2)
return results # Back to spaces
In Python 3, this immediately raises:
TabError: inconsistent use of tabs and spaces in indentation
Fixed approach — choose one convention:
# Option A: Convert everything to spaces (recommended)
def process_data(data):
results = []
for entry in data:
results.append(entry * 2)
return results
# Option B: Convert everything to tabs (less common)
def process_data(data):
\tresults = []
\tfor entry in data:
\t\tresults.append(entry * 2)
\treturn results
To fix mixed indentation globally, many editors offer a "Convert Indentation to Spaces" or "Convert Indentation to Tabs" command. You can also use Python's built-in tabnanny module to scan a file for ambiguous indentation:
python -m tabnanny my_script.py
If you want to automatically re-indent a file using spaces, reindent.py (part of the Python distribution) or third-party tools like black and autopep8 can help.
4. Unexpected Unindent When No Block Exists
Sometimes you reduce indentation thinking you are closing a block, but no outer block exists at that level. This often happens after editing code and accidentally deleting a line that opened the block.
Broken code:
print("Starting analysis...")
data = load_data() # Indented for no reason
print("Done.")
Python complains: IndentationError: unexpected indent. The fix is to remove the erroneous leading whitespace.
print("Starting analysis...")
data = load_data() # No indentation here
print("Done.")
5. Empty Blocks Without a Placeholder
If you need a block that does nothing (a stub for a function, an if branch, or an exception handler), you cannot leave it completely empty. Python requires at least one indented statement. Use the pass statement as a placeholder.
Broken code:
def future_feature():
# TODO: implement later
The comment is not a statement; Python sees an empty block and raises IndentationError: expected an indented block.
Fixed code:
def future_feature():
# TODO: implement later
pass
Alternatively, you can use ... (the ellipsis literal) which is a valid expression statement:
def future_feature():
...
6. Copy-Paste Artifacts and Invisible Characters
Pasting code from websites, PDFs, or word processors can introduce non-breaking spaces (Unicode U+00A0) or other whitespace characters that look like regular spaces but are not ASCII space (U+0020). Python only recognizes ASCII space and tab as indentation whitespace. These invisible intruders cause baffling IndentationError messages even when the code looks perfectly aligned.
Diagnosing invisible characters:
- Use
cat -Aon Linux/macOS to reveal non-ASCII whitespace. - In VS Code, open the file and look for small dots or unusual highlighting in the whitespace; you can also use the "Select Encoding" / "View Whitespace" features.
- Run a small Python script to detect non-standard characters:
with open('my_script.py', 'r', encoding='utf-8') as f:
for lineno, line in enumerate(f, 1):
if '\u00a0' in line:
print(f"Line {lineno} contains non-breaking space")
Replace these characters with regular spaces or tabs and the error will resolve.
7. Indentation Inside Multi-line Constructs
Indentation inside parentheses, brackets, or braces is purely cosmetic and does not affect block structure. However, confusion arises when a multi-line expression is combined with block indentation. The continuation lines can have arbitrary indentation as long as the block indentation itself remains consistent once the expression ends.
This is valid:
results = some_function(
argument_one,
argument_two,
argument_three
)
But this can cause an error if the closing parenthesis accidentally aligns with a different block:
if condition:
results = some_function(
argument_one,
argument_two
) # This dedent might confuse block parsing if not handled carefully
print(results)
The parser handles this correctly as long as the parentheses balance, but it's a common source of visual confusion. Keep bracket-aligned indentation consistent and use an opinionated formatter like black to avoid such pitfalls.
Systematic Debugging Workflow
When faced with an IndentationError, follow this step-by-step process to isolate and fix the problem quickly:
- Read the full traceback — note the exact line number and error message. Python tells you precisely where the mismatch was detected.
- Enable whitespace visualization in your editor. In VS Code:
View → Render Whitespaceor set"editor.renderWhitespace": "all". In Sublime Text:View → Show Whitespace. In Vim::set list. - Check the line above the reported line — the error often manifests on a line after the actual cause. If line 15 says "unindent does not match," examine lines 13–15 together.
- Run
python -m tabnannyon the file to get a second opinion on indentation ambiguity. - Select the entire block and re-indent using your editor's built-in commands (usually Tab/Shift-Tab or Ctrl+[/Ctrl+] in most editors). This normalizes all indentation to the same whitespace composition.
- Convert all indentation to spaces (or tabs, but spaces are the community standard). Most editors have a "Convert Indentation to Spaces" option in the command palette or bottom status bar.
- Delete and re-type problematic whitespace manually if invisible characters are suspected.
Best Practices to Prevent IndentationError
1. Choose Spaces Over Tabs (4 Spaces per Level)
PEP 8, the official Python style guide, recommends using 4 spaces per indentation level. This has become the de facto standard across the Python ecosystem. Configure your editor to insert 4 spaces when you press the Tab key. In VS Code, set:
{
"editor.insertSpaces": true,
"editor.tabSize": 4
}
In Vim, add to your .vimrc:
set expandtab
set tabstop=4
set shiftwidth=4
2. Use an Opinionated Code Formatter
Tools like Black (the uncompromising code formatter) or autopep8 automatically normalize indentation and eliminate mixed whitespace. Integrate them into your workflow:
# Install black
pip install black
# Format a file (or entire project)
black my_script.py
# Or use autopep8
pip install autopep8
autopep8 --in-place --aggressive my_script.py
Run these as part of a pre-commit hook or CI pipeline to catch indentation issues before they reach production.
3. Configure Your Editor's Indentation Detection
Modern editors can detect the indentation style of an existing file and adapt automatically. Enable this feature to avoid accidentally mixing styles when editing foreign code:
- VS Code:
"editor.detectIndentation": true(enabled by default) - Sublime Text: Preferences → Settings →
"detect_indentation": true - PyCharm: Settings → Editor → Code Style → Detect and use existing indentation
4. Add EditorConfig to Your Project
An .editorconfig file at the project root communicates indentation rules to any editor that supports it (nearly all do, often natively):
# .editorconfig
root = true
[*.py]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
This ensures that everyone contributing to the project uses the same indentation settings without manual configuration.
5. Avoid Inline Block Starters Without a Following Indented Line
When prototyping, you might write an if statement and intend to fill in the body later. Immediately add a placeholder to avoid leaving a syntactically invalid state:
# Risky — easy to forget and cause an error later
if condition:
# TODO
# Safer
if condition:
# TODO
pass
6. Be Mindful of Copy-Paste Sources
When copying code from a browser, PDF, or chat message, paste it into a plain-text editor first or use "Paste without formatting" (Ctrl+Shift+V in many applications) to strip hidden formatting. Better yet, paste into a temporary buffer in your editor and manually re-indent using the editor's native indentation commands.
7. Use Linters in Real Time
Integrate a linter like Pylint, Flake8, or Ruff into your editor. These tools flag indentation inconsistencies (and many other issues) as you type, before you even run the code:
# Install ruff (blazingly fast Python linter)
pip install ruff
# Check a file
ruff check my_script.py
# Auto-fix indentation issues
ruff check --fix my_script.py
Edge Cases and Advanced Scenarios
Indentation in Try/Except/Else/Finally Chains
All parts of a compound try statement must align at the same indentation level:
try:
risky_operation()
except ValueError:
handle_value_error()
except KeyError:
handle_key_error()
else:
cleanup_on_success()
finally:
always_cleanup()
A common mistake is indenting except or finally differently from the try keyword:
# Wrong — except indented more than try
try:
risky_operation()
except ValueError: # This will cause IndentationError
handle_value_error()
Indentation Inside Nested Functions and Classes
Deeply nested structures amplify indentation complexity. Use vertical whitespace (blank lines) to separate logical sections, but remember that blank lines do not affect indentation state:
class OuterClass:
def outer_method(self):
def inner_function(x):
if x > 0:
return x * 2
else:
return 0
return inner_function(self.value)
def another_method(self):
pass
Notice how each def and if/else aligns with its logical parent. Counting indentation levels manually becomes error-prone past 3–4 levels; consider refactoring deeply nested code into separate functions or methods.
Match/Case Statement Indentation (Python 3.10+)
The match statement introduces multiple indentation levels. The case blocks are indented one level under match, and the body of each case is indented one level further:
match command:
case "start":
begin_process()
case "stop":
halt_process()
case _:
raise ValueError("Unknown command")
Ensure case keywords align vertically and their bodies are consistently indented.
Automated Fixes and Tooling Summary
Here is a quick reference for commands that resolve indentation problems programmatically:
# 1. Scan for mixed tabs/spaces
python -m tabnanny my_script.py
# 2. Reindent a file to 4-space indentation (using Python's bundled script)
python -m reindent my_script.py
# 3. Format with Black (fully automatic)
black --line-length 88 my_script.py
# 4. Format with autopep8
autopep8 --in-place --aggressive --max-line-length 88 my_script.py
# 5. Lint with ruff and auto-fix
ruff check --fix my_script.py
# 6. Find non-breaking spaces and other invisible troublemakers
grep -P '\x{00a0}' my_script.py # Linux
python -c "import sys; [print(f'{i}: {repr(l)}') for i, l in enumerate(open(sys.argv[1]), 1) if '\u00a0' in l]" my_script.py
Conclusion
The IndentationError in Python, while frustrating at first encounter, is a direct consequence of the language's design philosophy: code readability matters, and structure should be visually unambiguous. Every cause — missing indentation, inconsistent levels, mixed tabs and spaces, invisible characters, or empty blocks — has a straightforward fix once you understand how Python's parser interprets whitespace. By adopting a consistent convention (4 spaces per level), configuring your editor properly, leveraging automated formatters and linters, and following a systematic debugging workflow, you can eliminate indentation errors from your development experience entirely. The discipline that Python enforces through indentation ultimately leads to cleaner, more maintainable code that is easier for both you and others to read, review, and reason about.