donnemartin/interactive-coding-challenges · error · TypeError
coins or total cannot be None
Error message
coins or total cannot be None
What it means
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.
Source
Thrown at recursion_dynamic/coin_change_min/coin_change_min_solution.ipynb:129
"metadata": {},
"source": [
"## Code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"\n",
"\n",
"class CoinChanger(object):\n",
"\n",
" def make_change(self, coins, total):\n",
" if coins is None or total is None:\n",
" raise TypeError('coins or total cannot be None')\n",
" if not coins or total == 0:\n",
" return 0\n",
" cache = {}\n",
" return self._make_change(coins, total, cache)\n",
"\n",
" def _make_change(self, coins, total, cache):\n",
" if total == 0:\n",
" return 0\n",
" if total in cache:\n",
" return cache[total]\n",
" min_ways = sys.maxsize\n",
" for coin in coins:\n",
" if total - coin < 0:\n",
" continue\n",
" ways = self._make_change(coins, total - coin, cache)\n",
" if ways < min_ways:\n",
" min_ways = ways\n",
" cache[total] = min_ways + 1\n",View on GitHub (pinned to 358f2cc604)
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
Example fix
// before changer.make_change(coins_from_config, total) # coins_from_config is None // after changer.make_change(coins_from_config or [], total)
Defensive patterns
Strategy: validation
Validate before calling
coins = coins or []
if total is None:
total = 0
changer.make_change(coins, total) Type guard
def is_coin_input(coins, total):
return isinstance(coins, list) and all(isinstance(c, int) and c > 0 for c in coins) and isinstance(total, int) Try / catch
try:
changer.make_change(coins, total)
except TypeError as e:
if 'coins or total cannot be None' in str(e):
raise ValueError('coins list and total are required') from e
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- input_items or total_weight cannot be None
- array cannot be None
- sentence cannot be None
- rows and cols cannot be None
- s or t cannot be None
AI-assisted analysis of donnemartin/interactive-coding-challenges@358f2cc604 (2026-08-28).
Data as JSON: /api/errors/43458454ca2051e3.
Report an issue: GitHub.