Skip to content

fix(core): Ensure exit code 0 when no_args_is_help shows help #1240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

Fabian665
Copy link

When no_args_is_help is True and no arguments are provided to a Typer application, the help message is displayed. However, an additional empty error panel also currently appears, and the application exits with status code 2. This issue affects applications using Click version 8.2.0 or later.

The root cause is a change in Click (pallets/click@d8763b93) where Command.parse_args now raises click.exceptions.NoArgsIsHelpError when no_args_is_help is true and no arguments are given. Previously, Click would print help and call ctx.exit() directly within parse_args.

Typer's generic except click.ClickException as e: block in core.py._main catches this NoArgsIsHelpError. The subsequent call to rich_utils.rich_format_error(e) (or e.show() if Rich is not used) results in the help message being displayed. This is because NoArgsIsHelpError is a subclass of click.UsageError, and UsageError.show() prints ctx.get_help(). However, this process also leads to an attempt to format a non-existent specific error message (as NoArgsIsHelpError.format_message() is empty), causing the blank error panel. Finally, sys.exit(e.exit_code) uses the UsageError's exit code of 2.

This commit addresses the issue by checking if the click.ClickException is of instance NoArgsIsHelpError and then raising click.exceptions.Exit(0)

This ensures that after the help message is displayed, the application exits cleanly with status code 0 and without displaying the erroneous empty error panel, aligning with the intended behavior of no_args_is_help.

Fabian665 and others added 2 commits June 5, 2025 16:20
When `no_args_is_help` is True and no arguments are provided to a Typer application, the help message is displayed. However, an additional empty error panel also currently appears, and the application exits with status code 2. This issue affects applications using Click version 8.2.0 or later.

The root cause is a change in Click (pallets/click@d8763b93) where `Command.parse_args` now raises `click.exceptions.NoArgsIsHelpError` when `no_args_is_help` is true and no arguments are given. Previously, Click would print help and call `ctx.exit()` directly within `parse_args`.

Typer's generic `except click.ClickException as e:` block in `core.py._main` catches this `NoArgsIsHelpError`. The subsequent call to `rich_utils.rich_format_error(e)` (or `e.show()` if Rich is not used) results in the help message being displayed. This is because `NoArgsIsHelpError` is a subclass of `click.UsageError`, and `UsageError.show()` prints `ctx.get_help()`. However, this process also leads to an attempt to format a non-existent specific error message (as `NoArgsIsHelpError.format_message()` is empty), causing the blank error panel. Finally, `sys.exit(e.exit_code)` uses the `UsageError`'s exit code of 2.

This commit addresses the issue by checking if the `click.ClickException` is of instance `NoArgsIsHelpError` and then raising `click.exceptions.Exit(0)`

This ensures that after the help message is displayed, the application exits cleanly with status code 0 and without displaying the erroneous empty error panel, aligning with the intended behavior of `no_args_is_help`.
@DHUKK
Copy link

DHUKK commented Jun 5, 2025

Should we also update the test that was changed here tests/test_tutorial/test_commands/test_index/test_tutorial003.py - test_no_arg

to add back the

assert result.exit_code == 0

@Fabian665
Copy link
Author

@DHUKK Thanks, I have restored the test.

I also added commented code for dropping Click<8.2 support.

@SeedyROM
Copy link

SeedyROM commented Jun 9, 2025

I'd love this to be merged, I think what's breaking the pipeline is that this issue is missing a label?

For now I'm just doing something akin to this!

First remove no_args_is_help from the options, then add this:

@app.callback(invoke_without_command=True)
def main(ctx: typer.Context):
    """My awesome CLI tool"""
    if ctx.invoked_subcommand is None:
        print(ctx.get_help())
        raise typer.Exit()

@Fabian665
Copy link
Author

@SeedyROM It doesn't let me put a label, I guess only maintainers can do so.

except click.ClickException as e:
# TODO: When deprecating Click < 8.2 remove this section [start]
_no_args_is_help_error = getattr(
Copy link

@diablodale diablodale Jun 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than probing attrs and instance, I suggest using the exception we want to catch.
Click's init.py doesn't pull in the NoArgsIsHelpError exception into itself. So instead, we can easily do the same at the top of this file with;

from click.exceptions import NoArgsIsHelpError

and then here simply the code to be

...
except KeyboardInterrupt as e:
    raise click.exceptions.Exit(130) from e
except NoArgsIsHelpError as e:
    raise click.exceptions.Exit(0) from e
except click.ClickException as e:
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants