donnemartin/interactive-coding-challenges · error · TypeError

num_steps cannot be None or negative

Error message

num_steps cannot be None or negative

What it means

Raised by Steps.count_ways when num_steps is None or negative. A single TypeError covers both invalid conditions — note the inconsistency with other modules that use ValueError for negatives — because the memoized recursion assumes a non-negative integer step count.

Source

Thrown at recursion_dynamic/steps/steps_solution.ipynb:117

  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Steps(object):\n",
    "\n",
    "    def count_ways(self, num_steps):\n",
    "        if num_steps is None or num_steps < 0:\n",
    "            raise TypeError('num_steps cannot be None or negative')\n",
    "        cache = {}\n",
    "        return self._count_ways(num_steps, cache)\n",
    "\n",
    "    def _count_ways(self, num_steps, cache):\n",
    "        if num_steps < 0:\n",
    "            return 0\n",
    "        if num_steps == 0:\n",
    "            return 1\n",
    "        if num_steps in cache:\n",
    "            return cache[num_steps]\n",
    "        cache[num_steps] = (self._count_ways(num_steps-1, cache) +\n",
    "                            self._count_ways(num_steps-2, cache) +\n",
    "                            self._count_ways(num_steps-3, cache))\n",
    "        return cache[num_steps]"
   ]
  },
  {
   "cell_type": "markdown",

View on GitHub (pinned to 358f2cc604)

Solutions

  1. Pass a non-negative int; clamp with max(0, num_steps)
  2. If migrating from other modules in this repo, remember negative raises TypeError here, not ValueError
  3. Validate/parse user input to int with a >= 0 check before calling

Example fix

// before
ways = s.count_ways(n if n else None)
// after
ways = s.count_ways(max(0, int(n or 0)))
Defensive patterns

Strategy: validation

Validate before calling

if num_steps is None or num_steps < 0:
    num_steps = 0
s.count_ways(num_steps)

Type guard

def is_nonneg_int(x):
    return isinstance(x, int) and x >= 0

Try / catch

try:
    s.count_ways(n)
except TypeError:  # note: TypeError covers negatives here too
    ways = 0

Prevention

When it happens

Trigger: Calling count_ways(None) or count_ways(-1). Zero is valid (handled inside _count_ways) and does not raise.

Common situations: Step counts computed from differences that can go negative (target - current); unvalidated query parameters; None defaults from config. Catching ValueError for negative input (as elsewhere) would miss this TypeError here.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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