python/cpython · error · TypeError

%r can't have docstrings

Error message

%r can't have docstrings

What it means

get_docstring only accepts node types that can syntactically carry a docstring: Module, ClassDef, FunctionDef, AsyncFunctionDef. Passing any other AST node (Expression, Assign, If, etc.) raises TypeError with the node's class name. Note this is about the node kind, not about whether a docstring exists — an eligible node without a docstring returns None instead of raising.

Source

Thrown at Lib/ast.py:340

        if isinstance(field, AST):
            yield field
        elif isinstance(field, list):
            for item in field:
                if isinstance(item, AST):
                    yield item


def get_docstring(node, clean=True):
    """
    Return the docstring for the given node or None if no docstring can
    be found.  If the node provided does not have docstrings a TypeError
    will be raised.

    If *clean* is `True`, all tabs are expanded to spaces and any whitespace
    that can be uniformly removed from the second line onwards is removed.
    """
    if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
        raise TypeError("%r can't have docstrings" % node.__class__.__name__)
    if not(node.body and isinstance(node.body[0], Expr)):
        return None
    node = node.body[0].value
    if isinstance(node, Constant) and isinstance(node.value, str):
        text = node.value
    else:
        return None
    if clean:
        import inspect
        text = inspect.cleandoc(text)
    return text


_line_pattern = None
def _splitlines_no_ff(source, maxlines=None):
    """Split a string into lines ignoring form feed and other chars.

    This mimics how the Python parser splits source code.

View on GitHub (pinned to bc6749cc3b)

Solutions

  1. Pass the module node or an actual def/class node: ast.get_docstring(tree) or ast.get_docstring(node) after isinstance filtering.
  2. Guard the call: if isinstance(node, (ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)).
  3. When iterating statements, skip non-docstring-bearing nodes instead of blindly forwarding them.

Example fix

# before
first = tree.body[0]
text = ast.get_docstring(first)  # first is an Assign -> TypeError

# after
for node in tree.body:
    if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
        text = ast.get_docstring(node)
        break
Defensive patterns

Strategy: type-guard

Validate before calling

DOCSTRING_NODES = (ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)

def docstring_of(node):
    if isinstance(node, DOCSTRING_NODES):
        return ast.get_docstring(node)
    return None

Type guard

def can_have_docstring(node) -> bool:
    return isinstance(node, (ast.Module, ast.ClassDef,
                             ast.FunctionDef, ast.AsyncFunctionDef))

Try / catch

try:
    text = ast.get_docstring(node)
except TypeError as e:
    if "can't have docstrings" in str(e):
        text = None  # node kind cannot carry a docstring; treat as absent

Prevention

When it happens

Trigger: ast.get_docstring(tree.body[0]) where the first statement is an Assign (module-level constant) rather than an Expr/FunctionDef; calling it on an Expression wrapper instead of the Module; feeding nodes from a custom visitor that yields non-def nodes.

Common situations: Docstring extractors iterating module bodies assuming the first statement is always a def/class; processing notebooks or generated code where a pragma or __future__ import precedes functions; tools that walk all nodes and call get_docstring unconditionally.

Related errors


AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14). Data as JSON: /api/errors/2765b7cf5d59bd30. Report an issue: GitHub.