Skip to content

bug: ValidArgsFunction receives partial input when argument contains colons in Bash #2376

Description

@michaeldcanady

Description

When using dynamic shell completion via ValidArgsFunction, splitting on colons : causes the input string toComplete to be truncated. Instead of receiving the full logical argument, the function receives only the segment after the last colon, while the prefix is moved into the args slice.

This makes implementing context-aware completions for URI-like schemes (e.g., local:/path, s3://bucket) or filesystem providers extremely brittle and non-portable across shells.

Minimum Viable Example (MVE)

package main

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
)

func main() {
	rootCmd := &cobra.Command{
		Use: "repro",
	}

	lsCmd := &cobra.Command{
		Use:   "ls <path>",
		Short: "Reproduction for colon split issue",
		Args:  cobra.MaximumNArgs(1),
		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
			// Log inputs to a file to inspect what the shell is sending
			f, _ := os.OpenFile("repro.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
			if f != nil {
				defer f.Close()
				_, _ = f.WriteString(fmt.Sprintf("args: %v, toComplete: %q\n", args, toComplete))
			}

			// We expect to see the full "local:/path" here to provide suggestions.
			// However, in Bash, "local:" ends up in 'args' and toComplete is just the path.
			return []string{"local:/tmp/file1", "local:/tmp/file2"}, cobra.ShellCompDirectiveNoFileComp
		},
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Printf("args: %v\n", args)
		},
	}

	rootCmd.AddCommand(lsCmd)
	if err := rootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}

Steps to Reproduce

  1. Build the reproduction: go build -o repro main.go
  2. Enable Bash completion: source <(./repro completion bash)
  3. Type the following and press TAB: ./repro ls local:/
  4. Inspect the generated repro.log.

Actual Behavior

The log shows:
args: [local :], toComplete: "/"

Bash has split the word at the colon. Cobra's completion logic follows this split, forcing the developer to manually reconstruct the original word from args and toComplete to understand the context (in this case, that the user is asking for completion from the local provider). which differs from usage with PreRunE and RunE which accept them as one argument

Expected Behavior

toComplete should ideally represent the full word as it does in PreRunE and RunE.

For example:
args: [], toComplete: "local:/"

Environment

  • Cobra Version: v1.10.2
  • OS: Linux
  • Shell: Bash 5.2+ (with bash-completion enabled)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions