PythonMastery
reference 3 min read · lesson 19 of 60 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 header line that ends with a colon needs at least one statement in its body. Python does not allow empty blocks, unlike languages where you can write {}.

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 you copy and paste code, and the indentation is removed or replaced by characters that Python doesn't recognise as indentation.

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 statement that does nothing. It is useful as a placeholder for code you will write later.

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). Three dots on their own line are also a valid statement:

python
def not_yet_implemented():
    ...

This is the usual style in type stubs and abstract method bodies.

When you'd see this in real code

  • Writing the outline of a class with several methods you plan to complete 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, so you still need pass.
  • Pasting code into a notebook or REPL, where the indentation of the lines was lost.

See Also

Found this useful? Share it with a friend who’s learning Python.