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
- Ensure the name variable is populated before calling the Add method; fail early in your own config loading if empty.
- Filter out empty strings when building names from a delimited list or map iteration.
- 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
- Validate tool names at config-load time and reject empty entries early.
- Skip or error on empty items when splitting delimited tool lists.
- Check map lookups for ok before using the returned name string.
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
- BuiltinPluginDirectories must contain only absolute paths
- URIConnection requires a non-empty URL
- err.Error()
- invalid tool name : tool names must match…
- Invalid tool name ' ': tool names must match…
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 orView on GitHub (pinned to cd8cf15dc3)