argparse default Not Validated Against choices (tested)
choices= on an argparse argument looks like it guarantees the final value is always one of the listed options. We measured what happens when default= itself isn't one of them — and choices= doesn't catch it.
1. An invalid default passes through with no error when the flag is omitted
import argparse
p = argparse.ArgumentParser()
p.add_argument("--color", choices=["red", "green", "blue"], default="purple")
args = p.parse_args([])
print(args.color)
purple
Measured: "purple" isn't in choices=["red", "green", "blue"], but running the program with no --color flag at all gives args.color == "purple" with no error, no warning. argparse only runs the choices check against values that actually came from the command line; it never validates default itself.
2. The exact same value is rejected the moment it's typed explicitly
try:
p.parse_args(["--color", "purple"])
except SystemExit as e:
print("SystemExit code:", e.code)
usage: prog [-h] [--color {red,green,blue}]
prog: error: argument --color: invalid choice: 'purple' (choose from red, green, blue)
SystemExit code: 2
Measured: the identical string "purple" that silently became the default value is rejected with a clear invalid choice error as soon as a user types --color purple themselves. This means a stale or typo'd default= — for example after renaming an entry in choices= but forgetting to update default= to match — stays invisible on the common path (no flag given) and only surfaces as a confusing error for the one case nobody would actually trigger on purpose.
3. Fix: assert the default is a member of choices at parser-build time
choices = ["red", "green", "blue"]
default = "purple"
assert default in choices, f"default {default!r} not in choices {choices}"
p2 = argparse.ArgumentParser()
p2.add_argument("--color", choices=choices, default=default)
AssertionError: default 'purple' not in choices ['red', 'green', 'blue']
Measured: argparse itself has no built-in option to validate default against choices at construction time. An explicit assert (or equivalent check) right next to the add_argument() call confirmed catching the mismatch immediately, at parser-definition time, instead of waiting for it to surface later as a silently wrong default or a misleading "invalid choice" error aimed at the wrong value.
Frequently asked questions
Does argparse validate that default= is one of the choices=?
No — we measured parse_args() accepting an invalid default silently when the corresponding flag is omitted from the command line; choices= is only checked against values actually typed by the user.
Why did my argparse default value cause no error, but the same value fails when I type it?
We confirmed argparse's choices check only runs on command-line input, never on the default itself, so an invalid default passes through silently until someone explicitly types that exact string.
How do I make sure my argparse default is valid against choices?
We verified there's no built-in check — adding an explicit assert (default in choices) next to add_argument() catches a mismatch at parser-definition time instead of letting it ship silently.
Is this an argparse bug?
Per the Python docs, choices= is documented to only validate parsed command-line values, not the default; this is the library's documented, working-as-intended behavior, not a bug.
What happens if I later remove a value from choices= but forget to update a matching default=?
We measured that scenario behaving exactly like this guide shows: the stale default keeps working silently when no flag is passed, with zero signal that it no longer matches choices=.
Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in argparse module: set default= to a value not in choices=, then compared parse_args([]) (no flag given) against parse_args() with that same invalid value passed explicitly on the command line. PlainHeaders is our extension; Python's argparse module is built in.