{"record":{"id":"fae17f62e19ef66d","repo":"donnemartin/interactive-coding-challenges","slug":"num-steps-cannot-be-none-or-negative","errorCode":null,"errorMessage":"num_steps cannot be None or negative","messagePattern":"num_steps cannot be None or negative","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"recursion_dynamic/steps/steps_solution.ipynb","lineNumber":117,"sourceCode":"  },\n  {\n   \"cell_type\": \"markdown\",\n   \"metadata\": {},\n   \"source\": [\n    \"## Code\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"class Steps(object):\\n\",\n    \"\\n\",\n    \"    def count_ways(self, num_steps):\\n\",\n    \"        if num_steps is None or num_steps < 0:\\n\",\n    \"            raise TypeError('num_steps cannot be None or negative')\\n\",\n    \"        cache = {}\\n\",\n    \"        return self._count_ways(num_steps, cache)\\n\",\n    \"\\n\",\n    \"    def _count_ways(self, num_steps, cache):\\n\",\n    \"        if num_steps < 0:\\n\",\n    \"            return 0\\n\",\n    \"        if num_steps == 0:\\n\",\n    \"            return 1\\n\",\n    \"        if num_steps in cache:\\n\",\n    \"            return cache[num_steps]\\n\",\n    \"        cache[num_steps] = (self._count_ways(num_steps-1, cache) +\\n\",\n    \"                            self._count_ways(num_steps-2, cache) +\\n\",\n    \"                            self._count_ways(num_steps-3, cache))\\n\",\n    \"        return cache[num_steps]\"\n   ]\n  },\n  {\n   \"cell_type\": \"markdown\",","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/donnemartin/interactive-coding-challenges/blob/358f2cc60426d5c4c3d7d580910eec9a7b393fa9/recursion_dynamic/steps/steps_solution.ipynb#L99-L135","documentation":"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.","triggerScenarios":"Calling count_ways(None) or count_ways(-1). Zero is valid (handled inside _count_ways) and does not raise.","commonSituations":"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.","solutions":["Pass a non-negative int; clamp with max(0, num_steps)","If migrating from other modules in this repo, remember negative raises TypeError here, not ValueError","Validate/parse user input to int with a >= 0 check before calling"],"exampleFix":"// before\nways = s.count_ways(n if n else None)\n// after\nways = s.count_ways(max(0, int(n or 0)))","handlingStrategy":"validation","validationCode":"if num_steps is None or num_steps < 0:\n    num_steps = 0\ns.count_ways(num_steps)","typeGuard":"def is_nonneg_int(x):\n    return isinstance(x, int) and x >= 0","tryCatchPattern":"try:\n    s.count_ways(n)\nexcept TypeError:  # note: TypeError covers negatives here too\n    ways = 0","preventionTips":["Remember negative raises TypeError here, not ValueError","Clamp step differences with max(0, ...)"],"tags":["python","recursion","memoization","dynamic-programming","input-validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"358f2cc60426d5c4c3d7d580910eec9a7b393fa9","analyzedAt":"2026-08-28T10:16:54.480Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}