My urfave/cli version is
latest main (commit fd14621)
Checklist
Dependency Management
- My project is using go modules.
Describe the bug
Persistent flags defined within MutuallyExclusiveFlags on a parent command are not propagated to subcommands. This is because the persistent flag inheritance loop in parseFlags (command_parse.go:39) iterates over pCmd.Flags instead of pCmd.allFlags().
allFlags() (defined in command.go:197) returns both cmd.Flags and all flags from cmd.MutuallyExclusiveFlags groups. Because parseFlags only walks pCmd.Flags, flags from MutuallyExclusiveFlags groups are never added to the subcommand's appliedFlags, making them unrecognised when passed after a subcommand name.
To reproduce
package main
import (
"context"
"fmt"
"os"
cli "github.com/urfave/cli/v3"
)
func main() {
cmd := &cli.Command{
Name: "root",
MutuallyExclusiveFlags: []cli.MutuallyExclusiveFlags{
{
Flags: [][]cli.Flag{
{&cli.StringFlag{Name: "alpha"}},
{&cli.StringFlag{Name: "beta"}},
},
},
},
Commands: []*cli.Command{
{
Name: "sub",
Action: func(_ context.Context, cmd *cli.Command) error {
fmt.Println(cmd.String("alpha"))
return nil
},
},
},
}
if err := cmd.Run(context.Background(), os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
Run with:
go run main.go sub --alpha hello
Observed behavior
flag provided but not defined: -alpha
Expected behavior
The --alpha flag should be recognised by the subcommand and print hello, since persistent flags (flags with Local defaulting to false) should propagate to subcommands regardless of whether they are defined in Flags or MutuallyExclusiveFlags.
A flag defined directly in Flags with the same configuration works correctly:
cmd := &cli.Command{
Name: "root",
Flags: []cli.Flag{
&cli.StringFlag{Name: "alpha"},
},
Commands: []*cli.Command{
{Name: "sub", Action: func(_ context.Context, cmd *cli.Command) error {
fmt.Println(cmd.String("alpha")) // prints "hello"
return nil
}},
},
}
Additional context
The fix is a one-line change in command_parse.go:39:
- for _, fl := range pCmd.Flags {
+ for _, fl := range pCmd.allFlags() {
Want to fix this yourself?
Yes, happy to submit a PR (#2266) with the fix and a test.
Run go version and paste its output here
go version go1.26.0 darwin/arm64
Run go env and paste its output here
AR='ar'
CC='cc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='c++'
GCCGO='gccgo'
GO111MODULE='on'
GOARCH='arm64'
GOARM64='v8.0'
GODEBUG=''
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOOS='darwin'
GOROOT='/opt/homebrew/opt/go/libexec'
GOVERSION='go1.26.0'
My urfave/cli version is
latest
main(commit fd14621)Checklist
Dependency Management
Describe the bug
Persistent flags defined within
MutuallyExclusiveFlagson a parent command are not propagated to subcommands. This is because the persistent flag inheritance loop inparseFlags(command_parse.go:39) iterates overpCmd.Flagsinstead ofpCmd.allFlags().allFlags()(defined incommand.go:197) returns bothcmd.Flagsand all flags fromcmd.MutuallyExclusiveFlagsgroups. BecauseparseFlagsonly walkspCmd.Flags, flags fromMutuallyExclusiveFlagsgroups are never added to the subcommand'sappliedFlags, making them unrecognised when passed after a subcommand name.To reproduce
Run with:
Observed behavior
Expected behavior
The
--alphaflag should be recognised by the subcommand and printhello, since persistent flags (flags withLocaldefaulting tofalse) should propagate to subcommands regardless of whether they are defined inFlagsorMutuallyExclusiveFlags.A flag defined directly in
Flagswith the same configuration works correctly:Additional context
The fix is a one-line change in
command_parse.go:39:Want to fix this yourself?
Yes, happy to submit a PR (#2266) with the fix and a test.
Run
go versionand paste its output hereRun
go envand paste its output here