microsoft/aspire · error · InvalidOperationException
Foundry CLI command ' ' could not be started.
Error message
Foundry CLI command '{command}' could not be started. What it means
RunProcessAsync starts the `foundry` CLI as a child process and throws this InvalidOperationException when Process.Start() returns false, i.e. the OS refused to launch the executable. Since CreateFoundryStartInfo uses UseShellExecute=false with the literal executable name "foundry", this almost always means the foundry executable could not be resolved or executed on PATH.
Solutions
- Install the Foundry Local CLI and verify `foundry --version` works in the same shell/environment that launches the AppHost.
- If installed, add its install directory to the PATH visible to the app host process (check IDE/launchSettings environment, not just your terminal).
- On Linux/macOS, ensure the binary has the executable bit: chmod +x $(which foundry).
- Restart the IDE/terminal after installation so it picks up the updated PATH.
Example fix
// before: launch profile without foundry on PATH fails to start the CLI
// after: extend the app host's PATH so `foundry` resolves (launchSettings.json):
// "environmentVariables": { "PATH": "/usr/local/bin:%PATH%" }
// verify first:
// $ foundry --version Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight check the user can run before launching the AppHost:
public static bool IsFoundryCliAvailable()
{
try
{
using var p = Process.Start(new ProcessStartInfo("foundry", "--version") { RedirectStandardOutput = true, UseShellExecute = false });
return p is not null;
}
catch (System.ComponentModel.Win32Exception)
{
return false;
}
} Try / catch
try
{
await foundry.StartAsync();
}
catch (InvalidOperationException ex) when (ex.Message.Contains("could not be started"))
{
throw new InvalidOperationException(
"The 'foundry' CLI was not found on PATH. Install Foundry Local and restart your terminal/IDE.", ex);
} Prevention
- Verify `foundry --version` resolves in the same environment that launches the AppHost (IDE, CI agent, container).
- Document the Foundry Local CLI as a prerequisite and fail fast with a clear check in dev scripts.
- After installing or updating the CLI, restart terminals and IDEs so PATH changes are picked up.
- On Linux/macOS, confirm the binary is executable after install.
When it happens
Trigger: Any RunFoundryCommandCoreAsync call (model load, model info, service start/stop, daemon-verb probe) where Process.Start() returns false — typically because no `foundry` (or foundry.exe) binary is on the PATH of the app-host process, or the file exists but is not executable.
Common situations: Foundry Local CLI never installed on the dev machine; installed via a mechanism that doesn't add it to PATH; the app host runs in an environment (IDE launch profile, CI agent, container) with a different PATH than the user shell; on Linux/macOS the binary lacks the execute bit.
Related errors
- Aspire skills bundle contains an empty relative path.
- Azure CLI (az) not found. Install it from…
- AzureCliNotOnPathException (Azure CLI is not on PATH)
- Failed to start Aspire dashboard
- Failed to start child process
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/7e0c3051d15e9fb7.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.Foundry/FoundryLocalService.cs:401
process,
FormatCommand(arguments),
onOutput,
cancellationToken,
stopReadingAfterProcessExit,
outputCompletionPredicate).ConfigureAwait(false);
}
internal static async Task<FoundryCommandResult> RunProcessAsync(
Process process,
string command,
Action<string>? onOutput,
CancellationToken cancellationToken,
bool stopReadingAfterProcessExit = false,
Func<string, bool>? outputCompletionPredicate = null)
{
if (!process.Start())
{
throw new InvalidOperationException($"Foundry CLI command '{command}' could not be started.");
}
using var cancellationRegistration = cancellationToken.Register(static state => KillProcess((Process)state!), process);
using var readCancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
var outputCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
void ProcessOutput(string line)
{
onOutput?.Invoke(line);
if (outputCompletionPredicate?.Invoke(line) is true)
{
outputCompletionSource.TrySetResult();
}
}
// Read both streams concurrently to avoid deadlock when a pipe buffer fills.
var outputTask = ReadOutputAsync(process.StandardOutput, ProcessOutput, cancellationToken, readCancellation.Token);
var errorTask = ReadOutputAsync(process.StandardError, ProcessOutput, cancellationToken, readCancellation.Token);View on GitHub (pinned to 25830f84bd)