github/copilot-sdk · error

invalid tool name: must not be empty

Error message

invalid %s tool name: must not be empty

What it means

validateToolName rejects empty tool names passed to the ToolSet builders. AddBuiltIn, AddCustom, and AddMCP all funnel through this validator, and an empty name would produce an un-addressable tool entry, so the library panics immediately with the tool kind in the message. Wildcard "*" and regex-valid names pass through.

Solutions

  1. Ensure the name variable is populated before calling the Add method; fail early in your own config loading if empty.
  2. Filter out empty strings when building names from a delimited list or map iteration.
  3. Pass a concrete valid tool name ([a-zA-Z0-9_-]+ or "*") instead of an empty string.

Example fix

// before
for _, n := range strings.Split(spec.Tools, ",") {
    ts.AddBuiltIn(n) // panics on ""
}

// after
for _, n := range strings.Split(spec.Tools, ",") {
    if n == "" { continue }
    ts.AddBuiltIn(n)
}
Defensive patterns

Strategy: validation

Validate before calling

func addableToolName(n string) bool { return n != "" }

Type guard

func nonEmpty(s string) bool { return s != "" }

Try / catch

defer func() {
    if r := recover(); r != nil {
        if s, ok := r.(string); ok && strings.Contains(s, "tool name: must not be empty") {
            log.Fatalf("tool registration failed: %s", s)
        }
        panic(r)
    }
}()

Prevention

When it happens

Trigger: Calling AddBuiltIn(""), AddCustom("") or AddMCP("") — typically when the name comes from a variable/config value that is an empty string, or from iterating a map with missing keys yielding "".

Common situations: Config file or environment value for a tool name missing so the lookup returns ""; a struct field not set before building the ToolSet; string splitting producing empty entries (e.g. strings.Split("a,,b", ",")).

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/27def267e94f05af. Report an issue: GitHub.

Appendix: source

Thrown at go/toolset.go:89

// AddMCP adds an MCP tool pattern. Matches tools advertised by any configured
// MCP server.
func (s *ToolSet) AddMCP(toolName string) *ToolSet {
	validateToolName("mcp", toolName)
	s.items = append(s.items, "mcp:"+toolName)
	return s
}

// ToSlice returns a defensive copy of the accumulated filter strings.
func (s *ToolSet) ToSlice() []string {
	out := make([]string, len(s.items))
	copy(out, s.items)
	return out
}

func validateToolName(kind, name string) {
	if name == "" {
		panic(fmt.Sprintf("invalid %s tool name: must not be empty", kind))
	}
	if name == "*" {
		return
	}
	if !toolNameRegex.MatchString(name) {
		panic(fmt.Sprintf(
			"invalid %s tool name %q: tool names must match /^[a-zA-Z0-9_-]+$/ or be the wildcard %q",
			kind, name, "*"))
	}
}

// BuiltInToolsIsolated lists built-in tools that operate only within the
// bounds of a single session — no host filesystem access outside the session,
// no cross-session state, no host environment access, no network. Safe to
// enable in [ModeEmpty] scenarios (e.g. multi-tenant servers) without leaking
// host capabilities.
//
// Contract: tools in this set MUST NOT be extended (even behind options or

View on GitHub (pinned to cd8cf15dc3)