donnemartin/interactive-coding-challenges · error · KeyError

word does not exist

Error message

word does not exist

What it means

Trie.remove raises KeyError('word does not exist') when find(word) returns None, meaning no terminating path spells the word. This includes words never inserted, words already removed, and prefixes that exist as node paths but not as complete words (terminates is False).

Source

Thrown at graphs_trees/trie/trie_solution.ipynb:226

    "    def insert(self, word):\n",
    "        if word is None:\n",
    "            raise TypeError('word cannot be None')\n",
    "        node = self.root\n",
    "        parent = None\n",
    "        for char in word:\n",
    "            if char in node.children:\n",
    "                node = node.children[char]\n",
    "            else:\n",
    "                node.children[char] = Node(char, parent=node)\n",
    "                node = node.children[char]\n",
    "        node.terminates = True\n",
    "\n",
    "    def remove(self, word):\n",
    "        if word is None:\n",
    "            raise TypeError('word cannot be None')\n",
    "        node = self.find(word)\n",
    "        if node is None:\n",
    "            raise KeyError('word does not exist')\n",
    "        node.terminates = False\n",
    "        parent = node.parent\n",
    "        while parent is not None:\n",
    "            # As we are propagating the delete up the \n",
    "            # parents, if this node has children, stop\n",
    "            # here to prevent orphaning its children.\n",
    "            # Or\n",
    "            # if this node is a terminating node that is\n",
    "            # not the terminating node of the input word, \n",
    "            # stop to prevent removing the associated word.\n",
    "            if node.children or node.terminates:\n",
    "                return\n",
    "            del parent.children[node.key]\n",
    "            node = parent\n",
    "            parent = parent.parent\n",
    "\n",
    "    def list_words(self):\n",
    "        result = []\n",

View on GitHub (pinned to 358f2cc604)

Solutions

  1. Check membership first: if trie.find(word) is not None: trie.remove(word), or note that find alone doesn't confirm termination — use a contains-style check
  2. Wrap remove in try/except KeyError for idempotent deletes
  3. Verify the word was actually inserted (and is a full word, not a prefix) before removal

Example fix

// before
trie.remove('foo')  # KeyError if absent
// after
try:
    trie.remove('foo')
except KeyError:
    pass  # already absent; deletion is idempotent
Defensive patterns

Strategy: try-catch

Validate before calling

if trie.find(word) is not None:
    trie.remove(word)

Type guard

def is_stored_word(trie, word) -> bool:
    node = trie.find(word)
    return node is not None and node.terminates

Try / catch

try:
    trie.remove(word)
except KeyError:
    pass  # already absent; keep deletes idempotent

Prevention

When it happens

Trigger: Calling trie.remove('foo') when 'foo' was never inserted; removing a word twice; removing a bare prefix like 'ab' when only 'abc' was inserted.

Common situations: Idempotent deletion code that assumes remove is safe to call repeatedly; deleting user-supplied words without checking membership; race where another thread already removed the word.

Related errors


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