Error Handling
Typer provides a multi-layered approach to error handling, distinguishing between unhandled developer exceptions and user-facing validation errors. It leverages the Rich library to provide "Pretty Exceptions" that are easier to debug, while also offering mechanisms for controlled application termination.
Pretty Exceptions
When an unhandled exception occurs during the execution of a Typer command, the library can intercept it and render a colorized, formatted traceback. This feature is powered by rich.traceback.Traceback and is enabled by default if Rich is installed.
How it Works
Typer implements this by overriding sys.excepthook. When a Typer app is called (via __call__ in typer/main.py), it installs a custom except_hook.
If an exception occurs, Typer attaches a DeveloperExceptionConfig object to the exception instance. This configuration contains settings defined in the Typer constructor. The except_hook then uses these settings to decide how to render the error.
# From typer/main.py
def except_hook(
exc_type: type[BaseException], exc_value: BaseException, tb: TracebackType | None
) -> None:
exception_config: DeveloperExceptionConfig | None = getattr(
exc_value, _typer_developer_exception_attr_name, None
)
# ... checks for configuration and environment variables ...
if HAS_RICH:
from . import rich_utils
rich_tb = rich_utils.get_traceback(exc, exception_config, internal_dir_names)
console_stderr = rich_utils._get_rich_console(stderr=True)
console_stderr.print(rich_tb)
return
Configuration
You can configure the behavior of pretty exceptions when initializing your Typer app:
pretty_exceptions_enable: Set toFalseto disable Rich-formatted tracebacks and revert to standard Python tracebacks.pretty_exceptions_show_locals: WhenTrue, the traceback will include the values of local variables for each frame, which is highly useful for debugging but should be used with caution if variables contain sensitive data.pretty_exceptions_short: WhenTrue(default), Typer and Click internal frames are suppressed from the traceback to focus on user code.
import typer
# Enable local variable display in tracebacks
app = typer.Typer(pretty_exceptions_show_locals=True)
@app.command()
def main(name: str = "morty"):
print(name + 3) # Raises TypeError
if __name__ == "__main__":
app()
Environment Override
Developers can force standard tracebacks by setting the TYPER_STANDARD_TRACEBACK environment variable. This is useful in CI/CD environments or when piping output to logs where ANSI color codes are not desired.
Validation and CLI Errors
Errors resulting from invalid user input (e.g., missing arguments, incorrect types) are handled differently. Typer catches click.ClickException and its subclasses (like BadParameter) and renders them in a consistent, user-friendly format.
In typer/core.py, the _main function intercepts these exceptions and uses rich_utils.rich_format_error to display them in a red panel:
# From typer/core.py
except click.ClickException as e:
if not standalone_mode:
raise
if HAS_RICH and rich_markup_mode is not None:
from . import rich_utils
rich_utils.rich_format_error(e)
else:
e.show()
sys.exit(e.exit_code)
This ensures that users see a clean error message and usage instructions rather than a full stack trace when they make a mistake using the CLI.
Manual Termination
Typer provides two specific exception classes for terminating the application early. These are re-exported from Click for convenience.
typer.Exit
Use typer.Exit() to stop the application immediately without printing a traceback or an error message. You can optionally provide an exit code (default is 0).
import typer
existing_usernames = ["rick", "morty"]
def maybe_create_user(username: str):
if username in existing_usernames:
print("The user already exists")
# Terminate silently with exit code 0
raise typer.Exit()
print(f"User created: {username}")
@app.command()
def main(username: str):
maybe_create_user(username=username)
typer.Abort
Use typer.Abort() to terminate the application with an "Aborted!" message. This is typically used when a user cancels an action (e.g., in a confirmation prompt) or when a critical but expected failure occurs.
import typer
app = typer.Typer()
@app.command()
def main(username: str):
if username == "root":
print("The root user is reserved")
# Terminate with "Aborted!" message and exit code 1
raise typer.Abort()
print(f"New user created: {username}")
When Rich is enabled, typer.Abort() is rendered using rich_utils.rich_abort_error(), which prints the text in red.