golang/go · error

bad tool name: %q

Error message

bad tool name: %q

What it means

Returned by base.ToolPath(toolName) when ValidToolName(toolName) is false. The go command builds the expected binary path as filepath.Join(build.ToolDir, toolName)+ToolExeSuffix, so a malformed tool name would produce a nonsense path; validation rejects it up front. An empty string or any character outside the accepted set trips the guard.

Source

Thrown at src/cmd/go/internal/base/tool.go:34

// Tool returns the path to the named builtin tool (for example, "vet").
// If the tool cannot be found, Tool exits the process.
func Tool(toolName string) string {
	toolPath, err := ToolPath(toolName)
	if err != nil && len(cfg.BuildToolexec) == 0 {
		// Give a nice message if there is no tool with that name.
		fmt.Fprintf(os.Stderr, "go: no such tool %q\n", toolName)
		SetExitStatus(2)
		Exit()
	}
	return toolPath
}

// ToolPath returns the path at which we expect to find the named tool
// (for example, "vet"), and the error (if any) from statting that path.
func ToolPath(toolName string) (string, error) {
	if !ValidToolName(toolName) {
		return "", fmt.Errorf("bad tool name: %q", toolName)
	}
	toolPath := filepath.Join(build.ToolDir, toolName) + cfg.ToolExeSuffix()
	err := toolStatCache.Do(toolPath, func() error {
		_, err := os.Stat(toolPath)
		return err
	})
	return toolPath, err
}

func ValidToolName(toolName string) bool {
	if toolName == "" {
		return false
	}
	for _, c := range toolName {
		switch {
		case 'a' <= c && c <= 'z', '0' <= c && c <= '9', c == '_':
		default:
			return false

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Validate with base.ValidToolName(name) before calling ToolPath.
  2. Use the known tool-name constants (e.g. "vet", "build") rather than constructing names from input.
  3. Trim and reject empty strings at the call site.
  4. If the name originates from argv/config, sanitize to alphanumeric+underscore before forwarding.

Example fix

// before
p, err := base.ToolPath(name) // panics-style error when name=""

// after
if !base.ValidToolName(name) {
    return fmt.Errorf("invalid tool name %q", name)
}
p, err := base.ToolPath(name)
Defensive patterns

Strategy: validation

Validate before calling

if !base.ValidToolName(name) {
    return fmt.Errorf("rejecting invalid tool name %q", name)
}
path, err := base.ToolPath(name)

Type guard

func isValidToolName(s string) bool {
    if s == "" { return false }
    for _, r := range s {
        if r == '/' || r == '\\' || r == ' ' || unicode.IsControl(r) {
            return false
        }
    }
    return true
}

Prevention

When it happens

Trigger: Calling base.ToolPath("") or with a name containing path separators, spaces, or characters not allowed by ValidToolName (which rejects empty and non-simple names).

Common situations: Code that builds tool names dynamically from user/file input; off-by-one slicing producing an empty string; passing a path instead of a bare tool name.

Related errors


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