donnemartin/interactive-coding-challenges · error · TypeError

root cannot be None

Error message

root cannot be None

What it means

Bst.find_second_largest raises TypeError('root cannot be None') when called on an empty tree (self.root is None). Finding the second largest node requires at least one existing node, so an empty tree is an invalid state for this operation.

Source

Thrown at graphs_trees/bst_second_largest/bst_second_largest_solution.ipynb:158

    "\n",
    "    def _find_second_largest(self, node):\n",
    "        if node.right is not None:\n",
    "            if node.right.left is not None or node.right.right is not None:\n",
    "                return self._find_second_largest(node.right)\n",
    "            else:\n",
    "                return node\n",
    "        else:\n",
    "            return self._find_right_most_node(node.left)\n",
    "\n",
    "    def _find_right_most_node(self, node):\n",
    "        if node.right is not None:\n",
    "            return self._find_right_most_node(node.right)\n",
    "        else:\n",
    "            return node\n",
    "\n",
    "    def find_second_largest(self):\n",
    "        if self.root is None:\n",
    "            raise TypeError('root cannot be None')\n",
    "        if self.root.right is None and self.root.left is None:\n",
    "            raise ValueError('root must have at least one child')\n",
    "        return self._find_second_largest(self.root)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Unit Test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {

View on GitHub (pinned to 358f2cc604)

Solutions

  1. Populate the tree with at least one insert() before calling find_second_largest()
  2. Guard the call: if tree.root is None: handle empty-tree case
  3. Return a sentinel (e.g. None) for empty trees by wrapping the call in your own method

Example fix

# before
bst = Bst()
second = bst.find_second_largest()  # empty tree

# after
bst = Bst()
for x in (10, 5, 20):
    bst.insert(x)
second = bst.find_second_largest() if bst.root else None
Defensive patterns

Strategy: validation

Validate before calling

if bst.root is None:
    return None  # or raise your own 'tree is empty' error
bst.find_second_largest()

Type guard

def is_non_empty_tree(t) -> bool:
    return t is not None and t.root is not None

Try / catch

try:
    second = bst.find_second_largest()
except TypeError as e:
    if 'root cannot be None' in str(e):
        second = None
    else:
        raise

Prevention

When it happens

Trigger: tree = Bst(); tree.find_second_largest() — calling the method before any insert() has been made, or after all nodes were removed by a future delete operation.

Common situations: Running queries on a freshly constructed tree, on a tree populated conditionally where the data path was empty, or ordering operations incorrectly (query before insert).

Related errors


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