{"record":{"id":"c0fc70c8e5b31e84","repo":"github/copilot-sdk","slug":"invalid-kind-tool-name-must-not-be-null-or-empt","errorCode":null,"errorMessage":"Invalid {kind} tool name: must not be null or empty.","messagePattern":"Invalid (.+?) tool name: must not be null or empty\\.","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"dotnet/src/ToolSet.cs","lineNumber":104,"sourceCode":"    /// Adds an MCP tool pattern. Matches tools advertised by any configured\n    /// MCP server.\n    /// </summary>\n    /// <param name=\"toolName\">The runtime's canonical wire name for the MCP\n    /// tool (e.g. <c>\"github-list_issues\"</c>), or <c>\"*\"</c> to match all\n    /// MCP tools from any server.</param>\n    /// <returns>This <see cref=\"ToolSet\"/> for chaining.</returns>\n    public ToolSet AddMcp(string toolName)\n    {\n        ValidateName(\"mcp\", toolName);\n        Add($\"mcp:{toolName}\");\n        return this;\n    }\n\n    private static void ValidateName(string kind, string name)\n    {\n        if (string.IsNullOrEmpty(name))\n        {\n            throw new ArgumentException(\n                $\"Invalid {kind} tool name: must not be null or empty.\",\n                nameof(name));\n        }\n        if (name == \"*\")\n        {\n            return;\n        }\n        if (!s_validToolName.IsMatch(name))\n        {\n            throw new ArgumentException(\n                $\"Invalid {kind} tool name '{name}': tool names must match /^[a-zA-Z0-9_-]+$/ \" +\n                \"or be the wildcard '*'.\",\n                nameof(name));\n        }\n    }\n}\n\n/// <summary>","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/dotnet/src/ToolSet.cs#L86-L122","documentation":"ToolSet.ValidateName rejects tool names that are null or the empty string when adding tools of any kind (built-in, custom, MCP). Every tool in the set must have a usable identifier; the wildcard \"*\" is checked next and allowed, but empty is never valid.","triggerScenarios":"Calling ToolSet.AddBuiltIn(null), AddCustom(\"\"), or AddMcp with an empty name — typically when a tool entry was constructed from data where the name field is missing or empty.","commonSituations":"MCP servers or config files listing tools without a name; deserialized tool metadata with null Name; string.Split/parse producing empty entries passed to Add*.","solutions":["Provide a non-empty name string to AddBuiltIn/AddCustom/AddMcp.","Filter or skip tool definitions with null/empty names before adding them.","Guard in your own loading code with string.IsNullOrEmpty and log/skip the offending entry.","Catch ArgumentException (paramName == \"name\") around batch tool registration to identify the bad entry."],"exampleFix":"// before\ntoolSet.AddCustom(config.Name); // config.Name == \"\"\n\n// after\nif (!string.IsNullOrEmpty(config.Name)) toolSet.AddCustom(config.Name);","handlingStrategy":"validation","validationCode":"if (string.IsNullOrEmpty(name))\n    throw new ArgumentException(\"Tool name must not be null or empty\", nameof(name));","typeGuard":"static bool IsValidToolNameBase(string? name) => !string.IsNullOrEmpty(name);","tryCatchPattern":"try { toolSet.AddCustom(name); }\ncatch (ArgumentException ex) when (ex.ParamName == \"name\" && ex.Message.Contains(\"must not be null or empty\"))\n{ logger.LogError(\"Skipping tool with blank name\"); }","preventionTips":["Skip tool definitions with null/empty names when loading from config or MCP","Validate metadata at deserialization time","Log skipped entries so missing names surface early"],"tags":["toolset","argument-validation","empty-string","dotnet"],"backgroundTag":"empty-required-field","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}