Exception Handling and Debugging
Typer is designed to provide a superior developer experience (DX) by transforming standard Python errors into readable, actionable feedback. This is achieved through two primary mechanisms: Pretty Exceptions, which use the Rich library to format stack traces, and Command Suggestions, which help users recover from typos.
Pretty Exceptions
By default, Typer intercepts unhandled exceptions to provide a "pretty" traceback. These tracebacks are color-coded, highlight the relevant code snippets, and can optionally display local variable values to aid debugging.
The Exception Hook Mechanism
Typer implements this by overriding the global sys.excepthook. When you call a Typer application instance (e.g., app()), the __call__ method in typer/main.py performs two critical actions:
- It sets
sys.excepthookto Typer's customexcept_hookfunction. - It wraps the command execution in a
try...exceptblock. If an exception occurs, it attaches aDeveloperExceptionConfigobject to the exception instance before re-raising it.
# From typer/main.py
def __call__(self, *args: Any, **kwargs: Any) -> Any:
if sys.excepthook != except_hook:
sys.excepthook = except_hook
try:
return get_command(self)(*args, **kwargs)
except Exception as e:
setattr(
e,
_typer_developer_exception_attr_name,
DeveloperExceptionConfig(
pretty_exceptions_enable=self.pretty_exceptions_enable,
pretty_exceptions_show_locals=self.pretty_exceptions_show_locals,
pretty_exceptions_short=self.pretty_exceptions_short,
),
)
raise e
This design ensures that the actual error remains the last thing shown in the terminal (improving DX), while the except_hook uses the attached configuration to determine how to render the traceback.
Configuration Options
You can configure exception behavior globally when initializing the Typer class:
pretty_exceptions_enable: Set toFalseto revert to standard Python tracebacks.pretty_exceptions_show_locals: WhenTrue, Rich will display the values of local variables for each frame in the stack trace. This is powerful for debugging but should be used cautiously if your code handles sensitive data (like passwords).pretty_exceptions_short: WhenTrue(the default), Typer simplifies the traceback by hiding or minimizing frames that originate from internal libraries likeclickandtyper, focusing the developer's attention on their own code.
import typer
app = typer.Typer(
pretty_exceptions_enable=True,
pretty_exceptions_show_locals=True,
pretty_exceptions_short=False, # Show full trace including internal frames
)
Environment Variable Overrides
Developers can force Typer to use standard Python tracebacks without changing the code by setting the TYPER_STANDARD_TRACEBACK environment variable. The except_hook checks this variable before attempting to use Rich:
# From typer/main.py
standard_traceback = os.getenv(
"TYPER_STANDARD_TRACEBACK", os.getenv("_TYPER_STANDARD_TRACEBACK")
)
if (
standard_traceback
or not exception_config
or not exception_config.pretty_exceptions_enable
):
_original_except_hook(exc_type, exc_value, tb)
return
Command Suggestions
When a user mistypes a subcommand, Typer provides helpful suggestions instead of just showing a "No such command" error. This feature is enabled by default and is implemented in the resolve_command method of the TyperGroup class in typer/core.py.
How Suggestions Work
Typer uses difflib.get_close_matches to compare the mistyped input against the list of registered commands. If a close match is found, it appends a "Did you mean...?" suggestion to the error message.
# From typer/core.py
def resolve_command(
self, ctx: click.Context, args: list[str]
) -> tuple[str | None, click.Command | None, list[str]]:
try:
return super().resolve_command(ctx, args)
except click.UsageError as e:
if self.suggest_commands:
available_commands = list(self.commands.keys())
if available_commands and args:
typo = args[0]
matches = get_close_matches(typo, available_commands)
if matches:
suggestions = ", ".join(f"{m!r}" for m in matches)
message = e.message.rstrip(".")
e.message = f"{message}. Did you mean {suggestions}?"
raise
Disabling Suggestions
If you prefer the strict behavior of the underlying Click library, you can disable suggestions in the Typer constructor:
app = typer.Typer(suggest_commands=False)
Internal Frame Filtering
To reduce noise, Typer's exception handling logic identifies frames originating from the typer or click packages. In the except_hook, these paths are calculated and passed to the renderer:
# From typer/main.py
typer_path = os.path.dirname(__file__)
click_path = os.path.dirname(click.__file__)
internal_dir_names = [typer_path, click_path]
When pretty_exceptions_short is enabled, the renderer uses these paths to suppress the display of internal library code, ensuring that the developer sees the error in the context of their own application logic.