[bug] aspire run crashes with 'Null character in path' when aspire.config.json records an invalid appHost.path (#17624 missed CreateSettingsFileAsync)
- Dominant language
- C#
- Stars
- 6.3k
- Forks
- 991
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 196
Description
`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):
```json
{"appHost":{"path":"First\u0000AppHost.csproj"}}
```
```console
$ 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:
```csharp
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.
Contributor guide
Research direction
Start in ProjectLocator.cs at IsValidConfiguredAppHostPath, GetAppHostProjectFileFromSettingsAsync, and CreateSettingsFileAsync around the cited lines. Reproduce the command with a NUL-containing appHost.path, then determine the intended write-path behavior—fail, warn and overwrite, or silently overwrite. Add tests covering the chosen behavior and confirm aspire run no longer crashes generically.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100