golang/go · error

tool %q is ambiguous; choose one of:

Error message

tool %q is ambiguous; choose one of:

What it means

`go tool <name>` matches tool directives by prefix. When more than one tool directive matches (len(matches) > 1) the command is ambiguous and the go command fatally prints this message listing the candidates. It calls base.Fatal, so it exits immediately.

Source

Thrown at src/cmd/go/internal/tool/tool.go:306

	modload.LoadModFile(ld, ctx)

	matches := []string{}
	for tool := range ld.MainModules.Tools() {
		if tool == name || defaultExecName(tool) == name {
			matches = append(matches, tool)
		}
	}

	if len(matches) == 1 {
		return matches[0]
	}

	if len(matches) > 1 {
		message := fmt.Sprintf("tool %q is ambiguous; choose one of:\n\t", name)
		for _, tool := range matches {
			message += tool + "\n\t"
		}
		base.Fatal(errors.New(message))
	}

	return ""
}

func builtTool(runAction *work.Action) string {
	linkAction := runAction.Deps[0]
	if toolN {
		// #72824: If -n is set, use the cached path if we can.
		// This is only necessary if the binary wasn't cached
		// before this invocation of the go command: if the binary
		// was cached, BuiltTarget() will be the cached executable.
		// It's only in the "first run", where we actually do the build
		// and save the result to the cache that BuiltTarget is not
		// the cached binary. Ideally, we would set BuiltTarget
		// to the cached path even in the first run, but if we
		// copy the binary to the cached path, and try to run it
		// in the same process, we'll run into the dreaded #22315

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Invoke the full tool path: `go tool foo/bar`.
  2. Run `go tool` with no args to list exact tool names, then copy one.
  3. Remove a redundant tool directive from go.mod if one is unwanted.

Example fix

# before
$ go tool foo        # matches foo/bar AND foo/baz -> ambiguous

# after
$ go tool foo/bar
Defensive patterns

Strategy: validation

Validate before calling

// Before invoking `go tool <name>`, ensure the name is unique among tools.
func uniqueTool(name string) (string, error) {
    out, err := exec.Command("go", "tool").Output()
    if err != nil { return "", err }
    var hits []string
    for _, t := range strings.Fields(string(out)) {
        if strings.HasPrefix(t, name) { hits = append(hits, t) }
    }
    if len(hits) != 1 { return "", fmt.Errorf("ambiguous tool %q: %v", name, hits) }
    return hits[0], nil
}

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Multiple `tool(...)` directives whose paths share a common prefix equal to the supplied argument (e.g. tools `foo/bar` and `foo/baz` invoked as `go tool foo`).

Common situations: Several tools from the same domain; shorthand invocation; copy-pasted tool directives.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/1e0743b4423d93da. Report an issue: GitHub.