{"record":{"id":"1e0743b4423d93da","repo":"golang/go","slug":"tool-q-is-ambiguous-choose-one-of","errorCode":null,"errorMessage":"tool %q is ambiguous; choose one of:","messagePattern":"tool %q is ambiguous; choose one of:","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmd/go/internal/tool/tool.go","lineNumber":306,"sourceCode":"\tmodload.LoadModFile(ld, ctx)\n\n\tmatches := []string{}\n\tfor tool := range ld.MainModules.Tools() {\n\t\tif tool == name || defaultExecName(tool) == name {\n\t\t\tmatches = append(matches, tool)\n\t\t}\n\t}\n\n\tif len(matches) == 1 {\n\t\treturn matches[0]\n\t}\n\n\tif len(matches) > 1 {\n\t\tmessage := fmt.Sprintf(\"tool %q is ambiguous; choose one of:\\n\\t\", name)\n\t\tfor _, tool := range matches {\n\t\t\tmessage += tool + \"\\n\\t\"\n\t\t}\n\t\tbase.Fatal(errors.New(message))\n\t}\n\n\treturn \"\"\n}\n\nfunc builtTool(runAction *work.Action) string {\n\tlinkAction := runAction.Deps[0]\n\tif toolN {\n\t\t// #72824: If -n is set, use the cached path if we can.\n\t\t// This is only necessary if the binary wasn't cached\n\t\t// before this invocation of the go command: if the binary\n\t\t// was cached, BuiltTarget() will be the cached executable.\n\t\t// It's only in the \"first run\", where we actually do the build\n\t\t// and save the result to the cache that BuiltTarget is not\n\t\t// the cached binary. Ideally, we would set BuiltTarget\n\t\t// to the cached path even in the first run, but if we\n\t\t// copy the binary to the cached path, and try to run it\n\t\t// in the same process, we'll run into the dreaded #22315","sourceCodeStart":288,"sourceCodeEnd":324,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/cmd/go/internal/tool/tool.go#L288-L324","documentation":"`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.","triggerScenarios":"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`).","commonSituations":"Several tools from the same domain; shorthand invocation; copy-pasted tool directives.","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."],"exampleFix":"# before\n$ go tool foo        # matches foo/bar AND foo/baz -> ambiguous\n\n# after\n$ go tool foo/bar","handlingStrategy":"validation","validationCode":"// Before invoking `go tool <name>`, ensure the name is unique among tools.\nfunc uniqueTool(name string) (string, error) {\n    out, err := exec.Command(\"go\", \"tool\").Output()\n    if err != nil { return \"\", err }\n    var hits []string\n    for _, t := range strings.Fields(string(out)) {\n        if strings.HasPrefix(t, name) { hits = append(hits, t) }\n    }\n    if len(hits) != 1 { return \"\", fmt.Errorf(\"ambiguous tool %q: %v\", name, hits) }\n    return hits[0], nil\n}","typeGuard":"null","tryCatchPattern":"null","preventionTips":["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."],"tags":["go-tool","toolchain","tool-directive","cli"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}