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
- Pass the module node or an actual def/class node: ast.get_docstring(tree) or ast.get_docstring(node) after isinstance filtering.
- Guard the call: if isinstance(node, (ast.Module, ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)).
- 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
- Filter nodes with isinstance before calling get_docstring in visitors.
- Remember eligible nodes without docstrings return None — no exception needed.
- Prefer calling get_docstring on the Module or def/class node directly.
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
- {obj!r} does not have annotations
- {obj!r}.__annotations__ is neither a dict nor None
- expected AST, got %r
- Not implemented
- Unknown thread safety level {level!r} for {name!r}. Valid le
AI-assisted analysis of python/cpython@bc6749cc3b (2026-08-14).
Data as JSON: /api/errors/2765b7cf5d59bd30.
Report an issue: GitHub.