PythonMastery
reference 3 min read · lesson 19 of 45 in Errors

IndentationError: expected an indented block

1 · The lesson

read

What this error means

Python opened a block with : (after if, for, def, class, while, try, else, with, etc.) but found no indented lines underneath it. Every colon-terminated header needs at least one statement in its body — Python does not allow empty blocks the way some languages do with {}.

When you see it

python
>>> def greet(name):
... 
  File "<stdin>", line 2
    
    ^
IndentationError: expected an indented block after function definition on line 1
python
>>> if x > 0:
... print(x)
  File "<stdin>", line 2
    print(x)
    ^
IndentationError: expected an indented block after 'if' statement on line 1

Why it happens

You wrote a header line and then either left it empty, forgot to indent the body, or deleted the body while refactoring and forgot to add a placeholder. It also happens when copy-pasting code where the indentation was stripped or replaced by something Python doesn't recognise.

How to fix it

1. Add a real body:

python
def greet(name):
    print("Hello,", name)

2. Use pass as a no-op placeholder:

python
def todo():
    pass

if x > 0:
    pass    # implement later
+ setup added so this can run · defines x
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

x = _AutoMock('x')

pass is a literal "do nothing" statement, perfect for stubbing out code you'll fill in.

3. Use a docstring — a string on its own line counts as a statement:

python
def greet(name):
    """Greet the user by name. Implementation pending."""

This is common in abstract methods, scaffolding, and placeholder classes:

python
class UserRepository:
    """Stores and retrieves users."""

    def find(self, user_id):
        """Return the user with the given id, or None."""

4. Use ... (Ellipsis) — a single dot-dot-dot is also a valid statement:

python
def not_yet_implemented():
    ...

This is idiomatic in type stubs and abstract method bodies.

When you'd actually see this in real code

  • Sketching out a class with several methods you plan to fill in later — every method needs pass, ..., or a docstring.
  • Commenting out the only line inside an if block while debugging — the block becomes empty. Comments are not statements; you still need pass.
  • Pasting code into a notebook or REPL where the indented lines didn't make it across.

See Also