donnemartin/interactive-coding-challenges · error · TypeError

key cannot be None

Error message

key cannot be None

What it means

Raised by Graph.add_node(key) when key is None. Nodes are stored in the self.nodes dict keyed by key, and None is treated as 'no key provided' rather than a hashable identifier. The guard uses TypeError because the argument type/value is fundamentally unusable as a node key.

Source

Thrown at graphs_trees/graph/graph_solution.ipynb:218

    "\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",
    "\n",
    "    def add_edge(self, source_key, dest_key, weight=0):\n",
    "        if source_key is None or dest_key is None:\n",
    "            raise KeyError('Invalid key')\n",
    "        if source_key not in self.nodes:\n",
    "            self.add_node(source_key)\n",
    "        if dest_key not in self.nodes:\n",
    "            self.add_node(dest_key)\n",
    "        self.nodes[source_key].add_neighbor(self.nodes[dest_key], weight)\n",
    "\n",
    "    def add_undirected_edge(self, src_key, dst_key, weight=0):\n",
    "        if src_key is None or dst_key is None:\n",
    "            raise TypeError('key cannot be None')\n",
    "        self.add_edge(src_key, dst_key, weight)\n",
    "        self.add_edge(dst_key, src_key, weight)"

View on GitHub (pinned to 358f2cc604)

Solutions

  1. Default missing keys to a real value or skip the record before calling add_node
  2. Use graph.nodes.get(key) lookups instead of None sentinels when threading values into add_node
  3. Add an assertion/validation layer over raw input data that rejects null keys early

Example fix

# before
graph.add_node(some_dict.get('id'))  # None if missing -> TypeError

# after
key = some_dict.get('id')
if key is not None:
    graph.add_node(key)
Defensive patterns

Strategy: validation

Validate before calling

if key is not None:
    graph.add_node(key)

Type guard

def is_valid_key(key):
    return key is not None

Try / catch

try:
    graph.add_node(key)
except TypeError:
    skip_record(key)  # or log and continue batch import

Prevention

When it happens

Trigger: Calling add_node(None); passing an optional variable that was never assigned; Graph.add_edge auto-invoking add_node(source_key) or add_node(dest_key) when one of those keys is None.

Common situations: Loading vertices from sparse JSON/DB rows where a key field is null; using None as a sentinel for 'missing node' and then feeding it into the graph; dict.get(key) returning None and being forwarded as a node key.

Related errors


AI-assisted analysis of donnemartin/interactive-coding-challenges@358f2cc604 (2026-08-28). Data as JSON: /api/errors/702802100bdcb2b6. Report an issue: GitHub.