microsoft/aspire · error · InvalidOperationException

The installed Foundry CLI does not expose a 'server' or…

Error message

The installed Foundry CLI does not expose a 'server' or 'service' command. Update Foundry Local and ensure the 'foundry' command on PATH is the expected installation.

What it means

FoundryLocalService needs to invoke the Foundry Local daemon, but different CLI versions name that subcommand differently ('service' in old versions, 'server' in new ones). At startup the library runs `foundry --help` and inspects the help text to pick the right verb. If neither 'server' nor 'service' appears in the help output, it throws this InvalidOperationException because it cannot control the daemon at all.

Solutions

  1. Install or update Foundry Local to a current version (e.g. via `pip install foundry-local` or the official installer) so the CLI exposes the daemon command
  2. Verify which binary resolves with `which foundry` / `Get-Command foundry` and remove or reorder a shadowing entry on PATH
  3. Run `foundry --help` manually and confirm a 'server' or 'service' line appears in the command list
  4. Reinstall Foundry Local cleanly if the help output is truncated or corrupted

Example fix

// before: 'foundry' on PATH is the wrong tool
$ foundry --help  # no 'server' or 'service' command listed
// after: install the real Foundry Local so it shadows the wrong binary
$ pip install -U foundry-local
$ foundry --help  # help now lists: server  Start, stop, restart, inspect...
Defensive patterns

Strategy: validation

Validate before calling

// Before running the AppHost, verify the CLI can drive the daemon:
var psi = new ProcessStartInfo("foundry", "--help") { RedirectStandardOutput = true };
using var p = Process.Start(psi)!;
var help = await p.StandardOutput.ReadToEndAsync();
bool ok = Regex.IsMatch(help, @"(?im)^\s*(?:Server:\s*)?server(?:\s|$)")
       || Regex.IsMatch(help, @"(?im)^\s*service(?:\s|$)");
if (!ok) { /* fix PATH / install Foundry Local before launching */ }

Prevention

When it happens

Trigger: DetermineDaemonVerb is called by GetDaemonVerbAsync with the output of `foundry --help`; the regexes `^\s*(?:Server:\s*)?server(?:\s|$)` and `^\s*service(?:\s|$)` both fail to match, typically because the `foundry` binary found on PATH is a different tool, a stub/shim, or a Foundry Local build whose help output omits both commands.

Common situations: Running the Aspire AppHost on a machine where Foundry Local is not installed so an unrelated 'foundry' executable shadows the real one on PATH; an outdated Foundry Local version whose help text does not list 'service'; a partially updated or broken install; localized or restructured CLI help output that no longer matches the expected patterns.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/433273f49e41a080. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting.Foundry/FoundryLocalService.cs:576

    }

    internal static string DetermineDaemonVerb(string helpOutput)
    {
        // Old CLI help lists:
        //   service  Commands to start and stop the Foundry Local service
        // New CLI help lists:
        //   server   Start, stop, restart, inspect, and troubleshoot the local Foundry daemon
        if (Regex.IsMatch(helpOutput, @"(?im)^\s*(?:Server:\s*)?server(?:\s|$)"))
        {
            return "server";
        }

        if (Regex.IsMatch(helpOutput, @"(?im)^\s*service(?:\s|$)"))
        {
            return "service";
        }

        throw new InvalidOperationException("The installed Foundry CLI does not expose a 'server' or 'service' command. Update Foundry Local and ensure the 'foundry' command on PATH is the expected installation.");
    }

    internal static bool TryParseServerEndpoint(string output, out Uri endpoint)
    {
        // Current CLI JSON output:
        //   {"running":true,"webUrls":["http://127.0.0.1:55829"],"port":55829}
        try
        {
            using var document = JsonDocument.Parse(output);
            if (document.RootElement.TryGetProperty("webUrls", out var webUrls) &&
                webUrls.ValueKind is JsonValueKind.Array &&
                webUrls.GetArrayLength() > 0 &&
                Uri.TryCreate(webUrls[0].GetString(), UriKind.Absolute, out var parsedEndpoint))
            {
                endpoint = EnsureTrailingSlash(parsedEndpoint);
                return true;
            }
        }

View on GitHub (pinned to 25830f84bd)