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 #22315View on GitHub (pinned to b6b368adc5)
Solutions
- Invoke the full tool path: `go tool foo/bar`.
- Run `go tool` with no args to list exact tool names, then copy one.
- 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
- Invoke tools by their full path rather than a shorthand prefix.
- Avoid registering two tool directives that share a prefix you use as a shorthand.
- Run `go tool` to review registered tool names.
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
- ${GoModToolVersion} is required for tool directives in go.mo
- named files must be .go files: %s
- %s: version must not be empty
- %s: all arguments must refer to packages in the same module
- %s: argument must be a package path, not a relative path
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/1e0743b4423d93da.
Report an issue: GitHub.