Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Bash & Python Automation By James Joyner IV · · 8 min read Last reviewed Jul 2026

Python Error Guide: 'IndentationError: unexpected indent' — Fix Whitespace and Blocks

Quick answer

Fix 'IndentationError: unexpected indent' in Python: resolve mixed tabs and spaces, stray leading whitespace, misaligned blocks, and paste artifacts cleanly.

  • #python
  • #automation
  • #troubleshooting
  • #errors
Free toolkit

Stuck on this Bash & Python Automation error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Overview

Python uses indentation to define code blocks, so whitespace is syntax. IndentationError: unexpected indent is raised at parse time — before any code runs — when a line is indented more than the parser expects with no block-opening statement (like if, for, def, or :) to justify it:

  File "deploy.py", line 12
    retries = 3
    ^
IndentationError: unexpected indent

The caret points at the first offending character. Because this is a SyntaxError subclass, the entire module fails to import — nothing executes, not even the lines above the error.

Symptoms

  • The script fails immediately on run or import with IndentationError, before any output.
  • The flagged line looks correctly aligned in your editor but Python still rejects it (a tabs-vs-spaces mismatch).
  • Code pasted from a terminal, chat, email, or a web page fails with unexpected indent.
  • A related message appears — unexpected indent, expected an indented block, unindent does not match any outer indentation level, or TabError: inconsistent use of tabs and spaces.
  • The error moves or disappears when you retype the line by hand.

Common Root Causes

  • Mixed tabs and spaces — some lines use tab characters and others use spaces; they can look identical on screen but the parser treats them as different indentation (raises TabError in Python 3).
  • Stray leading whitespace — an accidental space or tab at the start of a top-level line that shouldn’t be indented at all.
  • Paste artifacts — copying from a REPL (with >>> / ... prompts), a diff (with +/- prefixes), or a rich-text source that injects non-breaking spaces or smart indentation.
  • Misaligned block after an edit — moving a line in or out of a block without matching the surrounding indentation level.
  • Inconsistent indent width — mixing 2-space and 4-space indentation within the same block.
  • Non-breaking or zero-width characters — a U+00A0 (non-breaking space) or U+FEFF (BOM) masquerading as normal whitespace.

Diagnostic Workflow

First, make the parser tell you exactly where it fails and see the full traceback:

python -c "import ast, sys; ast.parse(open(sys.argv[1]).read())" deploy.py
Traceback (most recent call last):
  ...
  File "deploy.py", line 12
    retries = 3
    ^
IndentationError: unexpected indent

Reveal the actual whitespace bytes on the offending lines — tabs show as \t, non-breaking spaces as 302 240:

sed -n '10,14p' deploy.py | cat -A     # tabs show as ^I, line ends as $
sed -n '12p' deploy.py | od -c         # exact bytes, incl. \t and \240 (NBSP)

Ask Python to flag tab/space inconsistency directly:

python -tt deploy.py        # -tt makes inconsistent tab/space usage a hard error

Search the whole file for tab-indented lines if you standardize on spaces:

grep -nP '^\t' deploy.py         # lines that start with a tab
grep -nP '^ *\t| \t|\t ' deploy.py   # mixed space-then-tab or tab-then-space runs

Example Root Cause Analysis

An automation script failed in CI but “looked fine” in the developer’s editor:

  File "sync.py", line 21
    for item in queue:
IndentationError: unexpected indent

Line 21 appeared to line up with its neighbors. cat -A exposed the truth:

$ sed -n '19,22p' sync.py | cat -A
    logger.info("starting")$
^Ifor item in queue:$
^I    process(item)$
    done()$

Line 20 (logger.info) was indented with four spaces, but line 21 (for item) began with a tab (^I). The developer’s editor rendered a tab as four columns, so the two lines looked aligned, but Python saw a space-indented line followed by a tab-indented line at a different level — an unexpected indent. The fix was to convert the whole file to spaces and enforce it:

# Convert tabs to 4 spaces across the file
expand -t 4 sync.py > sync.py.tmp && mv sync.py.tmp sync.py

# Verify no tabs remain
grep -nP '\t' sync.py && echo "tabs still present" || echo "clean"

# Confirm it parses
python -c "import ast; ast.parse(open('sync.py').read())" && echo "parses OK"

The durable fix was configuring the editor and a formatter (black) so indentation can never drift again — PEP 8’s four-spaces, no tabs.

Prevention Best Practices

  • Use spaces, never tabs — follow PEP 8’s four-spaces-per-level and configure your editor to insert spaces when Tab is pressed.
  • Enable “show whitespace” in your editor so tabs and trailing whitespace are visible while editing.
  • Run a formatterblack (or ruff format) rewrites indentation consistently and makes this class of error nearly impossible.
  • Lint in CIruff/flake8 flags indentation and whitespace issues before the code merges.
  • Run Python with -tt in tests/CI so any tab/space inconsistency fails loudly.
  • Add an .editorconfig to the repo (indent_style = space, indent_size = 4) so every contributor’s editor agrees.
  • Strip paste prompts — when copying from a REPL or diff, remove >>> , ... , and +/- prefixes before pasting into a file.

Quick Command Reference

python -c "import ast,sys; ast.parse(open(sys.argv[1]).read())" file.py  # parse-only check
sed -n 'Np' file.py | cat -A       # show tabs (^I) and line ends ($)
sed -n 'Np' file.py | od -c        # exact bytes incl. \t and NBSP (\240)
python -tt file.py                 # make tab/space inconsistency fatal
grep -nP '\t' file.py              # find tab characters
expand -t 4 file.py > out && mv out file.py   # convert tabs to spaces
black file.py                      # auto-fix indentation

Conclusion

IndentationError: unexpected indent is a parse-time failure telling you a line is indented without a block that permits it — most often because tabs and spaces are mixed, or stray whitespace slipped in from a paste. Because the module won’t even import, the fix has to happen before the code runs: reveal the real whitespace with cat -A or od -c, standardize on four spaces, and let a formatter and linter enforce it. Configure your editor and an .editorconfig once, and this whole category of error stops recurring.

Free download · 368-page PDF

Fixed it? Get 500 Bash & Python Automation & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.