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.