Fix: Umgebungsvariablen aus launchSettings.json werden für ein Profil mit commandName: Executable ignoriert
Wenn Ihr Executable-Profil dotnet run oder dotnet watch startet, wendet der innere Befehl das Standardprofil an und überschreibt Ihre Variablen. Fügen Sie --no-launch-profile hinzu und verwenden Sie SDK 10.0.200+.
Wenn Ihr Profil mit commandName: "Executable" dotnet run oder dotnet watch run startet, fügen Sie --no-launch-profile (oder --launch-profile <name>) zu dessen commandLineArgs hinzu. Andernfalls wählt der innere Befehl das Standardprofil des Projekts, und dessen environmentVariables überschreiben die Werte, die Ihr Executable-Profil gerade gesetzt hat. Gibt die CLI “The launch profile type ‘Executable’ is not supported” aus, verwenden Sie ein SDK älter als 10.0.200. Aktualisieren Sie es, denn diese SDKs überspringen das gesamte Profil. Alles Folgende habe ich unter macOS mit den SDKs 10.0.112, 10.0.302, 10.0.401 und 11.0.100-rc.1 gemessen.
Der Fehler im Kontext
Das Problem tritt in zwei Varianten auf, und welche Sie treffen, hängt von Ihrem SDK ab.
Bei einem 10.0.1xx-SDK (und früheren SDKs, die nur Project-Profile kannten) meldet dotnet run --launch-profile das Problem direkt und führt das Projekt trotzdem aus:
Using launch settings from /src/app/Properties/launchSettings.json...
The launch profile "Exe" could not be applied.
The launch profile type 'Executable' is not supported.
MY_MODE=<null> DOTNET_ENVIRONMENT=<null> DOTNET_LAUNCH_PROFILE=<null> args=[] cwd=/src/app
Diese Meldung übersieht man leicht, weil die App startet. Sie startet nur ganz ohne Profil: keine Umgebungsvariablen, keine commandLineArgs, nicht einmal DOTNET_LAUNCH_PROFILE.
Ab 10.0.200 gibt es keine Warnung. Das Profil läuft, aber die App sieht die falschen Werte. Das ist der in dotnet/sdk#56023 gemeldete Fall: Ein Profil “Watch” setzt ASPNETCORE_ENVIRONMENT=Development, und die App meldet trotzdem Production. Der einzige Hinweis ist, dass die Zeile “Using launch settings” zweimal ausgegeben wird:
Using launch settings from /src/app/Properties/launchSettings.json...
Using launch settings from /src/app/Properties/launchSettings.json...
MY_MODE=from-Default DOTNET_ENVIRONMENT=Production DOTNET_LAUNCH_PROFILE=Default args=[] cwd=/src/app
Warum die Variablen verloren gehen
Die Ursachen, die häufigste zuerst:
- Ein verschachtelter SDK-Befehl wendet erneut ein Profil an.
dotnet runwendet ein Executable-Profil korrekt an. Es startetexecutablePathmit den gesetztenenvironmentVariablesdes Profils. Ist diese ausführbare Datei aberdotnetselbst (run,watch run), ist der Kindprozess ein brandneuesdotnet runohne--launch-profile. Es liest dieselbelaunchSettings.json, wählt das erste Profil mit unterstütztemcommandNameund setzt dessen Variablen auf dem App-Prozess. Profilvariablen haben Vorrang vor geerbten (sieheSetEnvironmentVariablesinRunCommand.cs), sodass Ihre äußeren Werte stillschweigend ersetzt werden. - Das SDK ist älter als 10.0.200. Die Executable-Unterstützung in
dotnet rununddotnet watchkam mit dotnet/sdk#51727, gemergt am 12. Dezember 2025 inrelease/10.0.2xx. Davor kannte die CLI nurcommandName: "Project". Visual Studio unterstützte Executable-Profile schon immer, weshalb dieselbe Datei “in VS funktioniert”. - Die IDE liest Executable-Profile nie. Die C#-Erweiterung für VS Code dokumentiert in ihren Debugger-Einstellungen: “Only profiles with
"commandName": "Project"are supported”. Die Auswahl eines Executable-Profils dort bewirkt für dessen Variablen nichts.
Wie dotnet run ein Profil wählt und Variablen schichtet
Es hilft, die genaue Reihenfolge der CLI zu kennen, denn jeder Workaround unten steuert lediglich einen dieser Schritte. Ab SDK 10.0.200 geht dotnet run so vor:
- Übergeben Sie
--no-launch-profile, verwendet es überhaupt kein Profil. Hier endet der Ablauf. - Andernfalls sucht es
Properties/launchSettings.json(My Project/launchSettings.jsonbei VB oder<app>.run.jsonneben einer dateibasierten App). - Mit
--launch-profile <name>wählt es dieses Profil. Die Suche unterscheidet zuerst Groß- und Kleinschreibung und fällt dann auf einen Treffer ohne Unterscheidung zurück. Ohne das Flag wählt es das erste Profil, dessencommandNameProjectoderExecutableist. Jeder andere Befehlsname (IISExpress,Docker,DotNetCore) wird übersprungen. - Es baut die Umgebung des Kindprozesses in drei Schichten auf. Zuerst die geerbte Umgebung des
dotnet-Prozesses. DannDOTNET_LAUNCH_PROFILE, dazuASPNETCORE_URLSausapplicationUrlbei Project-Profilen und jeder Eintrag inenvironmentVariables. Zuletzt jedes-e KEY=VALUEvon der Befehlszeile. Spätere Schichten gewinnen.
Schritt 4 ist der Grund, warum der verschachtelte Fall scheitert. Das äußere dotnet run legt Ihre Werte in Schicht eins des inneren dotnet run, und Schicht zwei des inneren Befehls ersetzt sie. Nichts im inneren Prozess weiß, dass er aus einem Startprofil gestartet wurde. Jede Lösung läuft darauf hinaus, dass sich im inneren Ablauf Schritt 1 oder Schritt 3 anders verhält.
Minimales Reproduktionsbeispiel
Eine Konsolen-App, die ausgibt, was sie tatsächlich erhalten hat:
// .NET 10, C# 14 - Program.cs (ImplicitUsings enabled)
Console.WriteLine($"MY_MODE={Environment.GetEnvironmentVariable("MY_MODE") ?? "<null>"} " +
$"DOTNET_ENVIRONMENT={Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "<null>"} " +
$"DOTNET_LAUNCH_PROFILE={Environment.GetEnvironmentVariable("DOTNET_LAUNCH_PROFILE") ?? "<null>"} " +
$"args=[{string.Join(",", args)}] cwd={Environment.CurrentDirectory}");
Dazu eine Properties/launchSettings.json mit einem normalen Project-Profil an erster Stelle, gefolgt von drei Executable-Profilen:
{
"profiles": {
"Default": {
"commandName": "Project",
"environmentVariables": { "MY_MODE": "from-Default", "DOTNET_ENVIRONMENT": "Production" }
},
"Exe": {
"commandName": "Executable",
"executablePath": "dotnet",
"commandLineArgs": "bin/Debug/net10.0/app.dll hello",
"workingDirectory": "..",
"environmentVariables": { "MY_MODE": "from-Exe", "DOTNET_ENVIRONMENT": "Development" }
},
"ExeDotnetRun": {
"commandName": "Executable",
"executablePath": "dotnet",
"commandLineArgs": "run --no-build",
"workingDirectory": "..",
"environmentVariables": { "MY_MODE": "from-ExeDotnetRun", "DOTNET_ENVIRONMENT": "Development" }
},
"Watch": {
"commandName": "Executable",
"executablePath": "dotnet",
"commandLineArgs": "watch run --non-interactive",
"workingDirectory": "..",
"environmentVariables": { "MY_MODE": "from-Watch", "DOTNET_ENVIRONMENT": "Development" }
}
}
}
dotnet run --no-build --launch-profile <name> lieferte auf jedem SDK folgende Ausgabe:
| Profil | 10.0.112 | 10.0.302 / 10.0.401 / 11.0.100-rc.1 |
|---|---|---|
Default (Project) | from-Default | from-Default |
Exe (startet app.dll) | Warnung “not supported”, <null> | from-Exe, Development, args [hello] |
ExeDotnetRun | Warnung “not supported”, <null> | from-Default, Production |
Watch | Warnung “not supported”, <null> | from-Default, Production (10.0.302) |
Die Zeile Exe zeigt, dass die Executable-Profilunterstützung auf modernen SDKs an sich funktioniert. Die Zeilen ExeDotnetRun und Watch zeigen das Überschreiben: Der innere Befehl meldet DOTNET_LAUNCH_PROFILE=Default, hat also von sich aus das erste Profil gewählt.
Die Lösung, Schritt für Schritt
-
SDK prüfen. Führen Sie
dotnet --versionim Projektverzeichnis aus, dennglobal.jsonkann ein älteres Band festlegen. Damitdotnet rununddotnet watchExecutable-Profile überhaupt berücksichtigen, benötigen Sie 10.0.200 oder höher. Behalten Sie die Variablen unter 10.0.1xx stattdessen in einemProject-Profil. -
Verhindern, dass der verschachtelte Befehl ein Profil wählt. Ist
executablePathgleichdotnetund beginnen die Argumente mitrunoderwatch, fügen Sie--no-launch-profilehinzu:// .NET SDK 10.0.200+ - Properties/launchSettings.json "Watch": { "commandName": "Executable", "executablePath": "dotnet", "commandLineArgs": "watch run --non-interactive --no-launch-profile", "workingDirectory": "..", "environmentVariables": { "MY_MODE": "from-Watch", "DOTNET_ENVIRONMENT": "Development" } }Mit dieser Änderung gab die App unter
dotnet watchauf 10.0.302MY_MODE=from-WatchNoProfile DOTNET_ENVIRONMENT=Developmentaus.DOTNET_LAUNCH_PROFILEzeigt weiterhin den Namen des äußeren Profils, weil das äußeredotnet runes gesetzt hat und nichts es überschrieb. -
Oder den verschachtelten Befehl auf ein bestimmtes Profil verweisen. Liegen die Variablen bereits in einem Project-Profil, verweisen Sie darauf, statt sie zu duplizieren:
// .NET SDK 10.0.200+ "ExeDotnetRunPinned": { "commandName": "Executable", "executablePath": "dotnet", "commandLineArgs": "run --no-build --launch-profile Dev", "workingDirectory": ".." }Das gab
MY_MODE=from-Dev DOTNET_ENVIRONMENT=Development DOTNET_LAUNCH_PROFILE=Devaus. In diesem Aufbau besitzt das innere Profil die Variablen. Alles, was Sie inenvironmentVariablesdes äußeren Profils eintragen, verliert, sobald beide Profile denselben Schlüssel setzen. -
In VS Code die Variablen nach
launch.jsonverschieben. Die C#-Erweiterung liest nur Project-Profile und davon nurenvironmentVariables,applicationUrlundcommandLineArgs. Legen Sie stattdessen einenenv-Block in einercoreclr-Startkonfiguration an. Werte inlaunch.jsonhaben ohnehin Vorrang vorlaunchSettings.json.
Stolperfallen und ähnliche Fälle
Ein an erster Stelle stehendes Executable-Profil wird zum Standard und kann endlos Prozesse erzeugen. Ab 10.0.200 ist das Standardprofil das erste, dessen commandName Project oder Executable ist (IsDefaultProfileType in LaunchSettings.cs, dieselbe Regel gilt in dotnet watch). Tragen Sie "commandLineArgs": "run --no-build" im ersten Profil ein und führen Sie ein einfaches dotnet run aus, wählt jedes Kind wieder dasselbe Profil. Auf 10.0.302 zählte ich nach 12 Sekunden 53 dotnet run-Prozesse, bevor ich sie beendete. Die oben beschriebene Lösung mit --no-launch-profile unterbricht auch diese Schleife. Ein Project-Profil am Anfang der Datei ist eine günstige Absicherung.
dotnet run -e überlebt den verschachtelten Schritt ebenfalls nicht. Ich habe dotnet run -e KEY=VALUE auf SDK 10.0.112 und später geprüft (siehe dotnet run -e). Es überschreibt das Profil für den Prozess, den das äußere dotnet run startet. Ist dieser Prozess ein weiteres dotnet run, überschreibt das innere Standardprofil auch diesen Wert: -lp ExeDotnetRun -e MY_MODE=from-cli gab weiterhin from-Default aus. Dasselbe gilt für einen einfachen Shell-Export. MY_MODE=from-shell dotnet run -lp Default gibt from-Default aus, weil Startprofilwerte stets Vorrang vor geerbten haben.
%VAR% wird expandiert, $(Property) (noch) nicht. Die CLI schickt jeden Wert durch Environment.ExpandEnvironmentVariables, daher funktioniert %HOME% auch unter macOS und Linux. $(HOME) und ${HOME} werden unverändert durchgereicht. MSBuild-Eigenschaften wie $(TargetPath) oder $(ProjectDir) werden auf keinem der von mir getesteten ausgelieferten SDKs (10.0.302, 10.0.401, 11.0.100-rc.1) expandiert. Statt einer stillschweigend ignorierten Variable erhalten Sie An error occurred trying to start process '$(TargetPath)' ... No such file or directory. Der ProjectLaunchTargetsProvider von Visual Studio expandiert sie (laut der Dokumentation zu Startprofilen im project-system), weshalb ein aus einem VS-Setup kopiertes Profil in der CLI scheitert. dotnet/sdk#56074 fügt die Expansion hinzu. Der Pull Request wurde am 4. September 2026 in main gemergt, ist aber heute weder in release/11.0.1xx-rc2 noch in einem 10.0-Band enthalten. Verwenden Sie bis zur Auslieferung relative Pfade.
workingDirectory ist relativ zum Ordner Properties, nicht zum Projekt. Die CLI löst es mit Path.Combine(Path.GetDirectoryName(launchSettingsPath), value) auf, sodass ".." das Projektverzeichnis bedeutet. Visual Studio und Rider lösen es anders auf, was dotnet/sdk#56129 derzeit diskutiert. Bei Project-Profilen ignoriert die CLI workingDirectory auf heutigen SDKs vollständig.
Eine Lösung für den verschachtelten Fall befindet sich im Review. dotnet/sdk#56087 lässt dotnet run auf Prozessen, die es aus einem Executable-Profil startet, einen Marker DOTNET_LAUNCH_PROFILE_APPLIED=1 setzen. Ein verschachteltes dotnet run ohne explizites Profil überspringt dann das Standardprofil. Am 30. September 2026 war der Pull Request noch offen. Selbst nach der Auslieferung deckt er nur Profile ab, die über die CLI gestartet werden. Laut PR benötigen IDEs, die das Executable-Profil direkt starten, weiterhin --no-launch-profile.
hotReloadEnabled in einem Project-Profil bewirkt in dotnet run nichts. Auch der Melder von #56023 hat das bemerkt. Hot Reload kommt von dotnet watch, nicht von einer Profileigenschaft. Genau deshalb verpacken viele dotnet watch überhaupt in ein Executable-Profil. Was der Watcher zusätzlich leistet, steht unter Unterschied zwischen dotnet watch und dotnet run.
Verwandte Artikel
- .NET 11 Preview 3: dotnet run -e setzt Umgebungsvariablen ohne Startprofile
- Was ist der Unterschied zwischen dotnet watch und dotnet run?
- Fix: dotnet watch Blazor Hot Reload WebSocket schlägt auf einer benutzerdefinierten lokalen Domain fehl, ein weiterer Fall, in dem Startprofil-Variablen den erwarteten Prozess nicht erreichen
- Eine dateibasierte C#-App mit
dotnet run app.csausführen, die<app>.run.json-Startprofile über denselben Code liest - Aspire zu einer bestehenden ASP.NET-Core-Solution hinzufügen, wo das eigene Startprofil des AppHost bestimmt, welche Umgebung jeder Dienst erhält
Quellen
- dotnet/sdk#56023:
launchSettings.jsonenvironment variables are not propagated forcommandName: Executable - dotnet/sdk#51727: Add Executable launch profile support to dotnet run and dotnet watch
- dotnet/sdk#56087: Preserve Executable launch profile environment in nested dotnet run
- dotnet/sdk#56074: Expand MSBuild properties across launch profiles
- dotnet/sdk#49131: Allow
dotnet runto use launch profiles withcommandName: Executable - dotnet/project-system: launch profiles documentation
- VS Code C# debugger settings: launchSettings.json support
dotnet runcommand reference on Microsoft Learn
Comments
Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.