{"record":{"id":"43458454ca2051e3","repo":"donnemartin/interactive-coding-challenges","slug":"coins-or-total-cannot-be-none","errorCode":null,"errorMessage":"coins or total cannot be None","messagePattern":"coins or total cannot be None","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"recursion_dynamic/coin_change_min/coin_change_min_solution.ipynb","lineNumber":129,"sourceCode":"   \"metadata\": {},\n   \"source\": [\n    \"## Code\"\n   ]\n  },\n  {\n   \"cell_type\": \"code\",\n   \"execution_count\": 1,\n   \"metadata\": {},\n   \"outputs\": [],\n   \"source\": [\n    \"import sys\\n\",\n    \"\\n\",\n    \"\\n\",\n    \"class CoinChanger(object):\\n\",\n    \"\\n\",\n    \"    def make_change(self, coins, total):\\n\",\n    \"        if coins is None or total is None:\\n\",\n    \"            raise TypeError('coins or total cannot be None')\\n\",\n    \"        if not coins or total == 0:\\n\",\n    \"            return 0\\n\",\n    \"        cache = {}\\n\",\n    \"        return self._make_change(coins, total, cache)\\n\",\n    \"\\n\",\n    \"    def _make_change(self, coins, total, cache):\\n\",\n    \"        if total == 0:\\n\",\n    \"            return 0\\n\",\n    \"        if total in cache:\\n\",\n    \"            return cache[total]\\n\",\n    \"        min_ways = sys.maxsize\\n\",\n    \"        for coin in coins:\\n\",\n    \"            if total - coin < 0:\\n\",\n    \"                continue\\n\",\n    \"            ways = self._make_change(coins, total - coin, cache)\\n\",\n    \"            if ways < min_ways:\\n\",\n    \"                min_ways = ways\\n\",\n    \"        cache[total] = min_ways + 1\\n\",","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/donnemartin/interactive-coding-challenges/blob/358f2cc60426d5c4c3d7d580910eec9a7b393fa9/recursion_dynamic/coin_change_min/coin_change_min_solution.ipynb#L111-L147","documentation":"CoinChanger.make_change (minimum coins to make a total, memoized via a cache dict) raises TypeError when coins or total is None. The guard precedes the trivial cases (empty coins or total == 0 return 0) and the recursive _make_change helper. It exists because iterating None coins or comparing None totals would produce obscure failures inside the DP.","triggerScenarios":"Calling CoinChanger().make_change(None, 11) or make_change([1,2,5], None); passing a coin list loaded from config where the denominations key was omitted, or a total from a failed int() parse.","commonSituations":"Denominations read from JSON/YAML where the field is optional; totals derived from amounts that can be None for 'not provided'; callers assuming empty-input cases ([]) are handled the same as None — they are not: [] returns 0 silently while None raises.","solutions":["Pass a concrete list of positive coin denominations and an int total","Treat missing config as empty list: coins = coins or [] — but decide whether that 'no coins' semantic (return 0) is what you want","Validate and convert inputs (int(total), list of ints) before calling make_change"],"exampleFix":"// before\nchanger.make_change(coins_from_config, total)  # coins_from_config is None\n// after\nchanger.make_change(coins_from_config or [], total)","handlingStrategy":"validation","validationCode":"coins = coins or []\nif total is None:\n    total = 0\nchanger.make_change(coins, total)","typeGuard":"def is_coin_input(coins, total):\n    return isinstance(coins, list) and all(isinstance(c, int) and c > 0 for c in coins) and isinstance(total, int)","tryCatchPattern":"try:\n    changer.make_change(coins, total)\nexcept TypeError as e:\n    if 'coins or total cannot be None' in str(e):\n        raise ValueError('coins list and total are required') from e\n    raise","preventionTips":["Distinguish None from [] semantics before calling (None raises, [] returns 0)","Validate denominations from config at load time","Convert totals with int() and handle parse failures explicitly"],"tags":["python","input-validation","typeerror","dynamic-programming"],"backgroundTag":"none-argument-typeerror","analyzedSha":"358f2cc60426d5c4c3d7d580910eec9a7b393fa9","analyzedAt":"2026-08-28T10:16:54.480Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}