Skip to content

[bug] aspire run crashes with 'Null character in path' when aspire.config.json records an invalid appHost.path (#17624 missed CreateSettingsFileAsync) #19137

Description

@adamint

aspire run crashes with Null character in path when aspire.config.json records an invalid appHost.path. #17624 fixed this for the read path, but the write path in ProjectLocator.CreateSettingsFileAsync resolves the same value without the guard that fix added.

Repro

Two AppHosts in a workspace, and an aspire.config.json whose appHost.path contains a NUL byte (legal in JSON, so it survives parsing):

{"appHost":{"path":"First\u0000AppHost.csproj"}}
$ aspire run --apphost SecondAppHost/SecondAppHost.csproj
❌ An unexpected error occurred: Null character in path. (Parameter 'path')

Reproduced on main (a50e553a8c) with a locally built CLI. No environment variables set. Any character in Path.GetInvalidPathChars() should behave the same.

Cause

The canonical readers validate before resolving. GetAppHostProjectFileFromSettingsAsync calls IsValidConfiguredAppHostPath first (ProjectLocator.cs:658 and :715), which rejects '\0' and Path.GetInvalidPathChars() precisely because Path.Combine / Path.GetFullPath throw ArgumentException on them. The comment at ProjectLocator.cs:653-657 cites #17624.

The upward config search in CreateSettingsFileAsync resolves the recorded path with no such guard:

var resolvedPath = Path.GetFullPath(
    Path.IsPathRooted(existingPath) ? existingPath : Path.Combine(configDir, existingPath));

ProjectLocator.cs:1124-1125 (line numbers as of 60e8e122f2). The ArgumentException escapes as a generic "An unexpected error occurred", which is the same symptom #17624 was filed for.

An explicit --apphost never runs the settings reader, so nothing validates the recorded string before this point.

Why this needs a design decision, not just a guard

IsValidConfiguredAppHostPath takes a silent flag and either displays an error or logs a warning. CreateSettingsFileAsync has no such flag, and it is a write path, so fixing it means deciding what should happen when a write encounters a malformed recorded value — fail, warn and overwrite, or silently overwrite. That choice is user-visible and deserves its own tests.

Notes

Found while working on #19080 (PR #19126). That PR does not touch this code path and adds no new unguarded resolution; it is flagged there so the crash is not misread as a regression. Filing separately because #17624 already missed this spot once, and a PR description stops being discoverable after merge.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions