dotnet run -nologo -- shots/pocket hands your program args = ["-nologo", "shots/pocket"].
dotnet run does not know the switch, and everything it does not know is forwarded to your
application.
-nologo is an MSBuild switch. dotnet run accepts only its own options and passes any
unrecognized token through to the app, so -nologo becomes args[0] and every positional
argument shifts by one. Put MSBuild switches on dotnet build and follow with
dotnet run --no-build, or use dotnet run -v q, which is a real dotnet run
option and quiets the build the same way.
The error
A console app that prints its arguments, run four ways:
> dotnet run -nologo -- a b
args: [-nologo] [a] [b]
> dotnet run --nologo -- a b
args: [--nologo] [a] [b]
> dotnet run -v q -- a b
args: [a] [b]
> dotnet run -p:Foo=1 -- a b
args: [a] [b]
Why it happens
dotnet run is lenient on purpose: an option it does not recognize is assumed to belong to the
application, so you can pass your own flags without the -- separator. The separator only marks where
the app's arguments definitely start; it does not turn earlier unknown tokens back into build switches. Options
dotnet run does own — -v, -c, -f, -p:,
--no-build — are consumed as you would expect.
What that did to the screenshot harness is the fun part. It reads its arguments by position:
// tools/shots/Program.cs
// Usage: dotnet run -- <outputDir> [baseUrl] [--checkin CODE] [--chat "message"]
var outDir = args.Length > 0 ? args[0] : "shots-out";
var baseUrl = args.Length > 1 && !args[1].StartsWith("--") ? args[1] : "http://localhost:5391";
With -nologo in front, the output folder became a directory literally named -nologo, and
the base URL became the shots folder. Playwright navigated Chromium to a local folder path, Chromium rendered its
directory listing, and the harness dutifully photographed it. No server was involved in that screenshot at all.
The fix
# MSBuild switches belong to the build; the run gets --no-build
dotnet build tools/shots -nologo
dotnet run --project tools/shots --no-build -- shots/pocket http://localhost:5159
# or let dotnet run quiet its own build
dotnet run --project tools/shots -v q -- shots/pocket http://localhost:5159
A defensive alternative inside the app: refuse any leading argument that starts with - and is not one
you defined. A harness that fails loudly on -nologo is better than one that invents a folder.
Where it bit us
Season three, Part 2 (tag pocket-02 in
the repo), while the season-two screenshot harness was
being taught to photograph the Pocket web host. One run came back with a directory listing where a phone-sized
home page should have been. The lesson, as the part put it: don't mix MSBuild switches into
dotnet run — and read the first screenshot of any harness change as suspiciously as the last.
Frequently asked
- Does dotnet run pass unknown options to my application?
- Yes. Any token dotnet run does not recognize as one of its own options is forwarded to the application's args, whether it appears before or after the -- separator. That is why an MSBuild switch such as -nologo arrives as args[0].
- How do I hide the build output from dotnet run?
- Use dotnet run -v q, which dotnet run understands and forwards to MSBuild as verbosity, or build first with dotnet build -nologo and then start the app with dotnet run --no-build so no build output is produced at all.
- Why did my app create a folder called -nologo?
- Because your app read args[0] as an output directory and dotnet run had forwarded -nologo as that first argument. Move the switch to dotnet build, and consider rejecting leading arguments that start with a dash and are not flags you defined.
More decoded errors in the Fixes category; the harness this came from starts at From Prompt to Polish, Part 1.