Repository navigation
Closed
Labels
area/v3relates to / is being considered for v3relates to / is being considered for v3kind/bugdescribes or fixes a bugdescribes or fixes a bugstatus/triagemaintainers still need to look into thismaintainers still need to look into this
Description
My urfave/cli version is
3.10.1
Checklist
- Are you running the latest v3 release? The list of releases is here.
- Did you check the manual for your release? The v3 manual is here
- Did you perform a search about this problem? Here's the GitHub guide about searching.
Dependency Management
- My project is using go modules.
Describe the bug
Subcommands don't seem to get passed their arguments correctly.
To reproduce
package bug_test
import (
"context"
"fmt"
"github.com/urfave/cli/v3"
)
func Example() {
_ = Root().Run(context.Background(), []string{"example", "sub", "value"})
//Output:
// value
}
func Root() *cli.Command {
return &cli.Command{
Name: "example",
Commands: []*cli.Command {
Sub(),
},
}
}
func Sub() *cli.Command {
return &cli.Command {
Name: "sub",
Arguments: []cli.Argument{
&cli.StringArg{
Name: "test",
},
},
Action: func(ctx context.Context, c *cli.Command) error {
fmt.Print(c.Args().First())
return nil
},
}
}Observed behavior
--- FAIL: Example (0.00s)
got:
want:
value
FAIL
FAIL github.com/tvanriel/minimal-example-urfave-cli-subcommand-bug 0.002s
FAIL
Expected behavior
I would have expected the included code to print value, as was passed in the arguments.
Additional context
Want to fix this yourself?
I did some digging and it seems to be related to a shadowed reference to rargs that gets set while parsing args and then cleaned before being saved in cmd.parsedArgs but couldn't get a fix to work.
We'd love to have more contributors on this project! If the fix for
this bug is easily explained and very small, feel free to create a
pull request for it.
Run go version and paste its output here
go version go1.26.4-X:nodwarf5 linux/amd64
Run go env and paste its output here
AR='ar'
CC='gcc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='g++'
GCCGO='gccgo'
GO111MODULE=''
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/home/tvanriel/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/tvanriel/.config/go/env'
GOEXE=''
GOEXPERIMENT='nodwarf5'
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3424211875=/tmp/go-build
-gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/home/tvanriel/Projects/Personal/minimal-example-urfave-cli-subcommand-bug/go.mod'
GOMODCACHE='/home/tvanriel/go/pkg/mod'
GONOPROXY='gitlab.figonet.nl,gitlab.bauwatch.tech'
GONOSUMDB='gitlab.figonet.nl,gitlab.bauwatch.tech'
GOOS='linux'
GOPATH='/home/tvanriel/go'
GOPRIVATE='gitlab.figonet.nl,gitlab.bauwatch.tech'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/lib/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/tvanriel/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/lib/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.26.4-X:nodwarf5'
GOWORK=''
PKG_CONFIG='pkg-config'
Metadata
Metadata
Assignees
Labels
area/v3relates to / is being considered for v3relates to / is being considered for v3kind/bugdescribes or fixes a bugdescribes or fixes a bugstatus/triagemaintainers still need to look into thismaintainers still need to look into this
Activity
[-]your bug title goes here[/-][+]subcommands don't get their arguments passed.[/+]BrandonIrizarry commentedon Jul 8, 2026
From what I can tell, this has nothing to do with subcommands per se - it seems to affect all commands. For example, the following minimal example works:
However, as soon as I name the argument (as what your example illustrates), it fails:
[-]subcommands don't get their arguments passed.[/-][+]Named arguments don't get passed in cmd.Args()[/+]BrandonIrizarry commentedon Jul 9, 2026
From looking at the code (see
command_run.go, at around line 363) I'm starting to realize something (correct me if I'm wrong): this behavior is expected. if you takecmd.Args()to refer to the slice of all non-named args.Say my command looks like this:
If I run
go run . boo bar, the output will be:Similarly, if you would like to refer to a range of arguments, you'd use something like this:
Output of
go run .:Output of
go run . boo:Output of
go run . boo bar:Output of
go run . boo bar bats:Output of
go run . boo bar bats fourth:Now we've hit cmd.Args() in this case.
That having been said, I did try to patch the code by changing this line (around line 379 in
command_run.go):to
I found that this does, in fact, get the behavior you and I expected (that is, that cmd.Args() should refer to the whole set of args at all times). However, this seems to break several tests. From what I can tell, the tests break because they don't set
cmd.Action, which then defaults to a "show help" action (again, I'm a bit fuzzy here, but that's what I observed.)PS: there's also an interesting note in the manual that alludes to my observation that the current behavior is the expected one:
Though at this point I'd also vouch for an update to the manual documenting "mixing named arguments with
cmd.Args()".dearchap commentedon Aug 16, 2026
This is intended behavior, not a bug.
When a command declares named arguments via the
Argumentsfield, each named argument consumes the positional arguments it needs from the command line.cmd.Args()returns only the arguments that were not consumed by a named argument. To retrieve the value of a named argument, usecmd.{Type}Arg(name), e.g.cmd.StringArg("test")(which returnsvaluein the reproduction from the issue).This behavior dates back to the original arguments feature (#1074) and is relied upon by the trailing
StringArgs{Max: -1}glob pattern documented in the manual.I've added a PR that documents this behavior clearly (#2411): it clarifies the
Args()doc comment, adds a "Mixing named arguments withcmd.Args()" section to the advanced arguments docs (with a runnable example), adds a note to the arguments basics page, and includes a test locking in the semantics.docs: clarify named Arguments are excluded from cmd.Args()