Skip to content

fix: treat a command name after "--" as an argument - #2534

Open
yxshee wants to merge 1 commit into
urfave:mainfrom
yxshee:fix/double-dash-subcommand
Open

yxshee wants to merge 1 commit into
urfave:mainfrom
yxshee:fix/double-dash-subcommand

Conversation

@yxshee

@yxshee yxshee commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

What type of PR is this?

  • bug

What this PR does / why we need it:

A -- ends flag parsing, and everything after it should be an argument of the command. That works for flags, but the first argument after -- is still looked up as a subcommand name:

cmd := &cli.Command{
	Name:     "app",
	Action:   func(_ context.Context, c *cli.Command) error { fmt.Println("args:", c.Args().Slice()); return nil },
	Commands: []*cli.Command{{Name: "sub", Action: func(context.Context, *cli.Command) error { fmt.Println("sub ran"); return nil }}},
}

On main, app -- sub prints sub ran, and app -- sub --x fails with flag provided but not defined: -x, because sub then parses --x as a flag. Expected: the root action runs with args: [sub] and args: [sub --x]. Shell completion already follows this rule: since #2412 it suggests nothing after a --, "only positional arguments are accepted after it", so app -- su<TAB> offers nothing but app -- sub runs the subcommand.

  • command_parse.go: parseFlags records when the parsed args start right after a -- (nothing positional came before it).
  • command_run.go: in that case the first arg is not looked up as a subcommand (or passed to SuggestCommandFunc). DefaultCommand still applies, so app -- foo behaves like app foo did before.
  • command.go: the unexported field that holds this state.
  • command_test.go: TestCommand_CommandNameAfterTerminatorIsAnArgument.

A -- that comes after a positional argument (app foo -- sub) already worked, since foo was taken as the first argument.

Which issue(s) this PR fixes:

None, found while checking -- handling with subcommands.

Testing

  • TestCommand_CommandNameAfterTerminatorIsAnArgument fails on main with flag provided but not defined: -notAFlag and the subcommand must not run when its name follows "--", and passes with this change.
  • make vet, make test, make test GFLAGS='--tags urfave_cli_no_template', make lint, make check-binary-size, make generate, make diffcheck and make v3diff pass, and golangci-lint run reports 0 issues.

Release Notes

A command name given after "--" is now passed to the command as an argument instead of running that subcommand.

A "--" ends flag parsing, and every argument after it is an argument
of the command. The first of them was still looked up as a subcommand
name, so "app -- cmd --x" ran the cmd subcommand, which then parsed
--x as a flag.
@yxshee
yxshee requested a review from a team as a code owner November 8, 2026 21:12

This branch has not been deployed

No deployments
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.

1 participant