microsoft/aspire · error · InvalidOperationException
Cannot materialize terminal hosts: AppHost:FilePath /…
Error message
Cannot materialize terminal hosts: AppHost:FilePath / AppHost:Path is not set in configuration.
What it means
MaterializeTerminalHostsAsync runs at AppHost start and needs the AppHost's own file path from configuration to lay out per-run terminal host files/sockets. This InvalidOperationException is thrown when neither AppHost:FilePath nor AppHost:Path is present in configuration, meaning the terminal host materialization cannot compute its target locations.
Solutions
- Run the AppHost through the standard entry point (aspire run / DistributedApplication.Run) which sets AppHost:FilePath
- Set configuration["AppHost:FilePath"] (or AppHost:Path) to the AppHost project path in custom hosts/tests before starting
- Verify no custom IConfiguration setup removes or renames the AppHost configuration section
Example fix
// before (custom test host) var app = builder.Build(); await app.RunAsync(); // after builder.Configuration["AppHost:FilePath"] = appHostProjectPath; var app = builder.Build(); await app.RunAsync();
Defensive patterns
Strategy: validation
Validate before calling
var appHostPath = config["AppHost:FilePath"] ?? config["AppHost:Path"];
if (string.IsNullOrEmpty(appHostPath))
throw new InvalidOperationException("AppHost:FilePath must be set before enabling terminals in custom hosts."); Try / catch
try { await MaterializeTerminalHostsAsync(@event, ct); }
catch (InvalidOperationException ex) when (ex.Message.Contains("AppHost:")) { logger.LogError(ex, "Terminal host materialization skipped: AppHost path missing from configuration"); } Prevention
- Run AppHosts through the standard aspire run entry point
- Set AppHost:FilePath explicitly in custom hosts and test harnesses
- Avoid rebuilding IConfiguration in ways that drop AppHost keys
When it happens
Trigger: Running an AppHost with WithTerminal-configured resources in an environment where the AppHost configuration keys AppHost:FilePath/AppHost:Path were never set — e.g. custom hosting of the DistributedApplication, test harnesses, or trimming of AppHost configuration.
Common situations: Hosting the AppHost programmatically in tests or tools instead of via the standard aspire run pipeline, or configuration being rebuilt/overwritten before the BeforeStartEvent fires.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- AppHost path not found in configuration.
- ASPIRE_REMOTE_APPHOST_TOKEN environment variable is not set
- Cannot resolve the isolated browser user data directory…
- Cannot set both UseDeveloperCertificate and Certificate…
- Clustering has not been configured for this service.
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/70074643fda72e7d.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting/TerminalResourceBuilderExtensions.cs:178
if (replicaCount < 1)
{
replicaCount = 1;
}
// All per-replica terminal-host files live flat under ~/.aspire/trmnl/, with
// a random per-run replica id. This:
// - matches the repo's convention for per-user runtime state (cf. ~/.aspire/cli/bch,
// ~/.aspire/dev-certs, ~/.aspire/deployments)
// - avoids dropping UDS sockets in the global /tmp on Linux where different distros
// treat /tmp permissions differently
// - keeps absolute paths short enough to fit sun_path (104 bytes on macOS)
// - lets external tools enumerate by listing {trmnlDir}/{id}.metadata.json
// - prevents an old child or AppHost cleanup from touching a replacement run's sockets.
var configuration = @event.Services.GetRequiredService<IConfiguration>();
var appHostPath = configuration["AppHost:FilePath"] ?? configuration["AppHost:Path"];
if (string.IsNullOrEmpty(appHostPath))
{
throw new InvalidOperationException(
"Cannot materialize terminal hosts: AppHost:FilePath / AppHost:Path is not set in configuration.");
}
var trmnlDirectory = configuration[TerminalHostPaths.DirectoryOverrideConfigName];
if (string.IsNullOrEmpty(trmnlDirectory))
{
var homeDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
trmnlDirectory = TerminalHostPaths.GetTrmnlDirectory(homeDirectory);
}
// 0700 on Unix so other local users cannot enumerate which terminals exist on
// this machine. On Windows the user-profile ACLs (per-user by default) make this
// a no-op; CreateDirectory is idempotent.
try
{
DirectoryHelper.CreateWithOwnerOnlyPermissions(trmnlDirectory);
}
catch (Exception ex) when (ex is UnauthorizedAccessException or IOException)View on GitHub (pinned to 25830f84bd)