Skip to content

Custom shell completion doesn't work, root.Before() not being called #2348

@TLINDEN

Description

@TLINDEN

My urfave/cli version is

v3.9.1-0.20260524212652-be8b79d0c8de

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

Custom shell completion still doesn't work because root.Before() is not being called. So my config struct is nil and the function which is being called to generated the completion slice panics.

To reproduce

func main() {
	conf := &cfg.Config{}

	cmd := &cli.Command{
		Commands: []*cli.Command{
			IndexShow(conf),
		},

		Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
			if err := conf.Init(); err != nil {
				if len(os.Args) > 1 {
					return nil, err
				} else {
					fmt.Println(cmd.UsageText)
					return nil, nil
				}
			}

			log.Init(conf)

			return nil, nil
		},
	}
}

func IndexShow(conf *cfg.Config) *cli.Command {
	return &cli.Command{
		Name:    "show",
		Aliases: []string{"sh"},
		Usage:   "show details about an index",

		Action: func(ctx context.Context, cmd *cli.Command) error {
			index := cmd.Args().Get(0)
			if index == "" {
				return errors.New("no index specified")
			}

			return es.IndexShow(conf, cmd.Args().Get(0))
		},

		ShellComplete: func(ctx context.Context, cmd *cli.Command) {
			if cmd.NArg() > 0 {
				return
			}

			indices, err := es.IndexNames(conf)
			if err != nil {
				return
			}

			for _, index := range indices {
				fmt.Println(index)
			}
		},
	}
}

Observed behavior

The Before() in the top level cli is not being called and the config struct is nil when shell complete is being executed. It then crashes:

./esctl index show --generate-shell-completion
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x30 pc=0x1a5aa41]

goroutine 1 [running]:
codeberg.org/scip/esctl/pkg/es.IndexNames(0x1bf3740?)
        /home/scip/dev/esctl/pkg/es/index.go:38 +0x21
codeberg.org/scip/esctl/cmd.Index.IndexShow.func2({0x1f38e6b?, 0x4?}, 0xc00022b110?)
        /home/scip/dev/esctl/cmd/index.go:144 +0x34
github.com/urfave/cli/v3.checkCompletions({0x21bc5d0, 0xc000266420}, 0xc00023d8c8)
        /home/scip/go/pkg/mod/github.com/urfave/cli/v3@v3.9.1-0.20260524212652-be8b79d0c8de/help.go:515 +0x24e
github.com/urfave/cli/v3.(*Command).run(0xc00023d8c8, {0x21bc5d0, 0xc000266420}, {0xc000268ed0, 0x1, 0x1})
        /home/scip/go/pkg/mod/github.com/urfave/cli/v3@v3.9.1-0.20260524212652-be8b79d0c8de/command_run.go:161 +0xb7b
github.com/urfave/cli/v3.(*Command).run(0xc00023f8c8, {0x21bc5d0, 0xc000266300}, {0xc00007dd20, 0x2, 0x2})
        /home/scip/go/pkg/mod/github.com/urfave/cli/v3@v3.9.1-0.20260524212652-be8b79d0c8de/command_run.go:292 +0x20b6
github.com/urfave/cli/v3.(*Command).run(0xc000245608, {0x21bc5d0, 0xc0000a1dd0}, {0xc000020100, 0x4, 0x4})
        /home/scip/go/pkg/mod/github.com/urfave/cli/v3@v3.9.1-0.20260524212652-be8b79d0c8de/command_run.go:292 +0x20b6
github.com/urfave/cli/v3.(*Command).Run(...)
        /home/scip/go/pkg/mod/github.com/urfave/cli/v3@v3.9.1-0.20260524212652-be8b79d0c8de/command_run.go:93
codeberg.org/scip/esctl/cmd.Main()
        /home/scip/dev/esctl/cmd/root.go:119 +0x127b
main.main()
        /home/scip/dev/esctl/main.go:26 +0x13

Expected behavior

I'd expect that the top level cli is being executed.

My current workaround is to load the config directly in the SHellComplete: function.

Want to fix this yourself?

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.25.0 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/scip/.cache/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/home/scip/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2427390874=/tmp/go-build -gno-record-gcc-switches'
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMOD='/home/scip/dev/esctl/go.mod'
GOMODCACHE='/home/scip/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/scip/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/home/scip/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.0.linux-amd64'
GOSUMDB='sum.golang.org'
GOTELEMETRY='off'
GOTELEMETRYDIR='/home/scip/.config/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/home/scip/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.25.0.linux-amd64/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.25.0'
GOWORK=''
PKG_CONFIG='pkg-config'

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/v3relates to / is being considered for v3kind/bugdescribes or fixes a bugstatus/triagemaintainers still need to look into this

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions