donnemartin/interactive-coding-challenges · error · TypeError

neighbor or weight cannot be None

Error message

neighbor or weight cannot be None

What it means

Raised by Node.add_neighbor(neighbor, weight) in graph_solution.ipynb when either neighbor or weight is None. The graph implementation stores weights keyed by neighbor in adj_weights/adj_nodes dicts, so a None neighbor has no .key and None weight would poison shortest-path arithmetic. The guard rejects the call with TypeError before any mutation happens.

Source

Thrown at graphs_trees/graph/graph_solution.ipynb:196

    "\n",
    "class Node:\n",
    "\n",
    "    def __init__(self, key):\n",
    "        self.key = key\n",
    "        self.visit_state = State.unvisited\n",
    "        self.incoming_edges = 0\n",
    "        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",

View on GitHub (pinned to 358f2cc604)

Solutions

  1. Coerce None weight to the default: use weight = weight if weight is not None else 0 (or call add_edge without the weight argument)
  2. Ensure the neighbor object is created and registered via Graph.add_node before add_neighbor
  3. Validate edge data at load time and reject/skip rows with missing endpoints or weights

Example fix

# before
node_a.add_neighbor(node_b, None)  # TypeError

# after
weight = 0 if weight is None else weight
node_a.add_neighbor(node_b, weight)
Defensive patterns

Strategy: validation

Validate before calling

if neighbor is not None and weight is not None:
    node.add_neighbor(neighbor, weight)
# or normalize: node.add_neighbor(neighbor, 0 if weight is None else weight)

Type guard

def is_valid_neighbor(neighbor, weight):
    return neighbor is not None and weight is not None

Try / catch

try:
    node.add_neighbor(neighbor, weight)
except TypeError as e:
    if 'cannot be None' in str(e):
        skip_or_log_edge(neighbor, weight)
    else:
        raise

Prevention

When it happens

Trigger: Calling add_neighbor(None, 5), add_neighbor(node, None), or relying on the default weight=0 while explicitly passing weight=None. Also triggered indirectly by Graph.add_edge if it ever forwards a None weight or a None-valued node lookup.

Common situations: Building graphs from sparse data (CSV/JSON) where some edge weights are missing and default to None; passing an uninitialized node variable; calling add_edge(src, dst, weight=None) expecting the default weight to kick in.

Related errors


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