microsoft/aspire · error · InvalidOperationException

Toolbox ' ' must contain at least one tool.

Error message

Toolbox '{name}' must contain at least one tool.

What it means

Thrown by FoundryToolboxReconciler.Create when the tools collection is empty (after null checks). A Toolbox must declare at least one tool definition; creating one with zero tools would produce a useless remote Toolbox, so it is rejected eagerly as an invalid operation.

Solutions

  1. Add at least one tool definition (WithMcpTool, AddAiSearchTool, or a Bing grounding tool) before creating the Toolbox.
  2. If tools come from configuration, validate the list is non-empty before calling Create and surface a clear startup error.
  3. If conditional tool registration is intentional, guard the Toolbox creation itself behind the same condition.

Example fix

// before
var tools = config.EnabledTools.Select(...).ToArray(); // empty when nothing enabled
var toolbox = FoundryToolboxReconciler.Create("tb", desc, tools, metadata);
// after
if (tools.Count == 0) throw new InvalidOperationException("No tools enabled in configuration; cannot create Toolbox.");
var toolbox = FoundryToolboxReconciler.Create("tb", desc, tools, metadata);
Defensive patterns

Strategy: validation

Validate before calling

if (tools is not { Count: > 0 })
{
    throw new InvalidOperationException("At least one tool must be registered before creating a Foundry Toolbox.");
}

Type guard

static bool HasTools(IReadOnlyList<FoundryToolDefinition> tools) => tools.Count > 0;

Try / catch

try
{
    var toolbox = FoundryToolboxReconciler.Create(name, description, tools, metadata);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("must contain at least one tool"))
{
    logger.LogError(ex, "Toolbox {Name} was created with no tools.", name);
    throw;
}

Prevention

When it happens

Trigger: Calling Create (via AddFoundryToolbox-style builders) with an empty tools list — e.g. conditionally-added tools were all skipped, or the caller forgot to add any With*Tool calls.

Common situations: Building the tool list from configuration where all entries were filtered out; refactoring that removed tool registrations; feature flags disabling every tool at runtime.

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 microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/1cadc4ab00b244da. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Foundry/Toolbox/FoundryToolboxReconciler.cs:64

    public IReadOnlyDictionary<string, string> Metadata { get; }

    public string ConfigurationHash { get; }

    public static FoundryToolboxDeploymentDefinition Create(
        string name,
        string description,
        IReadOnlyList<ResolvedFoundryToolboxTool> tools,
        IReadOnlyDictionary<string, string> metadata)
    {
        ArgumentException.ThrowIfNullOrEmpty(name);
        ArgumentException.ThrowIfNullOrWhiteSpace(description);
        ArgumentNullException.ThrowIfNull(tools);
        ArgumentNullException.ThrowIfNull(metadata);

        if (tools.Count == 0)
        {
            throw new InvalidOperationException($"Toolbox '{name}' must contain at least one tool.");
        }

        var duplicateToolNames = tools
            .GroupBy(tool => tool.Name, StringComparer.Ordinal)
            .Where(group => group.Count() > 1)
            .Select(group => group.Key)
            .Order(StringComparer.Ordinal)
            .ToArray();

        if (duplicateToolNames.Length > 0)
        {
            throw new InvalidOperationException(
                $"Toolbox '{name}' contains duplicate tool names: {string.Join(", ", duplicateToolNames)}.");
        }

        var duplicateMcpServerLabels = tools
            .Where(tool => tool.McpServerLabel is not null)
            .GroupBy(tool => tool.McpServerLabel!, StringComparer.Ordinal)

View on GitHub (pinned to 25830f84bd)