donnemartin/interactive-coding-challenges · error · TypeError
neighbor cannot be None
Error message
neighbor cannot be None
What it means
Raised by Node.remove_neighbor(neighbor) in graph_solution.ipynb when neighbor is None. The method dereferences neighbor.key to look up and delete entries in adj_weights and adj_nodes, so a None neighbor would crash with AttributeError; the guard converts that into an explicit TypeError.
Source
Thrown at graphs_trees/graph/graph_solution.ipynb:203
" self.adj_nodes = {} # Key = key, val = Node\n",
" self.adj_weights = {} # Key = key, val = weight\n",
"\n",
" def __repr__(self):\n",
" return str(self.key)\n",
"\n",
" def __lt__(self, other):\n",
" return self.key < other.key\n",
"\n",
" def add_neighbor(self, neighbor, weight=0):\n",
" if neighbor is None or weight is None:\n",
" raise TypeError('neighbor or weight cannot be None')\n",
" neighbor.incoming_edges += 1\n",
" self.adj_weights[neighbor.key] = weight\n",
" self.adj_nodes[neighbor.key] = neighbor\n",
"\n",
" def remove_neighbor(self, neighbor):\n",
" if neighbor is None:\n",
" raise TypeError('neighbor cannot be None')\n",
" if neighbor.key not in self.adj_nodes:\n",
" raise KeyError('neighbor not found')\n",
" neighbor.incoming_edges -= 1\n",
" del self.adj_weights[neighbor.key]\n",
" del self.adj_nodes[neighbor.key]\n",
"\n",
"\n",
"class Graph:\n",
"\n",
" def __init__(self):\n",
" self.nodes = {} # Key = key, val = Node\n",
"\n",
" def add_node(self, key):\n",
" if key is None:\n",
" raise TypeError('key cannot be None')\n",
" if key not in self.nodes:\n",
" self.nodes[key] = Node(key)\n",
" return self.nodes[key]\n",View on GitHub (pinned to 358f2cc604)
Solutions
- Check the neighbor object is not None before calling remove_neighbor
- Verify the node exists via graph.nodes.get(key) and skip when the lookup returns None
- Sanitize adjacency lists at graph-build time so None nodes never enter the structure
Example fix
# before
node.remove_neighbor(maybe_none) # TypeError
# after
if maybe_none is not None:
node.remove_neighbor(maybe_none) Defensive patterns
Strategy: validation
Validate before calling
if neighbor is not None:
node.remove_neighbor(neighbor) Type guard
def is_removable(node, neighbor):
return neighbor is not None and neighbor.key in node.adj_nodes Try / catch
try:
node.remove_neighbor(neighbor)
except TypeError:
pass # nothing to remove
except KeyError as e:
if 'neighbor not found' in str(e):
pass # already removed
else:
raise Prevention
- Guard both None and key-in-adj_nodes before removing
- Keep a single source of truth for edge existence instead of manual dual-direction removals
When it happens
Trigger: Calling remove_neighbor(None) directly, or removing an edge where the destination node was never created (variable still None). Also hit when iterating a list of neighbors that contains None entries from malformed graph construction.
Common situations: Tearing down edges from untrusted datasets; test code that removes a neighbor it never added; refactor where the node variable name holds None after a failed lookup in self.nodes.
Related errors
- neighbor or weight cannot be None
- neighbor not found
- key cannot be None
- graph cannot be None
- Input node keys cannot be None
AI-assisted analysis of donnemartin/interactive-coding-challenges@358f2cc604 (2026-08-28).
Data as JSON: /api/errors/ce824aae052bd24c.
Report an issue: GitHub.