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

  1. Run the AppHost through the standard entry point (aspire run / DistributedApplication.Run) which sets AppHost:FilePath
  2. Set configuration["AppHost:FilePath"] (or AppHost:Path) to the AppHost project path in custom hosts/tests before starting
  3. 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

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


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)