InvalidOperationException: This instance has already started one or more requests. Properties can only be modified before sending the first request. is thrown when you change a property such as Timeout or BaseAddress on an HttpClient that has already sent a request. Set those properties once, when the client is created, and leave them alone afterwards.

Move the property assignments into the object initializer, as in the fix below, so they are all set before the first request leaves the client.

The error

PS ...\Cli> dotnet run --no-build -- started
200 from /api/v1/patients: Maria Garcia, David Chen
Unhandled exception. System.InvalidOperationException: This instance has already started one or more requests. Properties can only be modified before sending the first request.
   at System.Net.Http.HttpClient.CheckDisposedOrStarted()
   at System.Net.Http.HttpClient.set_Timeout(TimeSpan value)
   at Program.<Main>$(String[] args) in ...\Cli\Program.cs:line 40
   at Program.<Main>(String[] args)

The code that threw:

using var http = new HttpClient();
http.BaseAddress = new Uri("http://127.0.0.1:15801/api/v1/");
Console.WriteLine(await http.GetStringAsync("patients"));
http.Timeout = TimeSpan.FromSeconds(30);

Why it happens

Setting BaseAddress before the first call worked; the first line of output is the test server's answer. Setting Timeout after that call threw. The stack trace names the check: set_Timeout calls HttpClient.CheckDisposedOrStarted, and once the instance has sent a request the setter refuses the change. The message itself states the rule: properties can only be modified before sending the first request.

In a larger program the two lines can sit far apart: a client kept in a field, and a method that sets Timeout on it. The run above shows what decides the outcome: a property set before the first request was accepted, and the one set after a request was refused, so look for any assignment that can run after the client has been used.

The fix

using var http = new HttpClient
{
    BaseAddress = new Uri("http://127.0.0.1:15801/api/v1/"),
    Timeout = TimeSpan.FromSeconds(30),
};
Console.WriteLine(await http.GetStringAsync("patients"));
Console.WriteLine(await http.GetStringAsync("patients"));
PS ...\Cli> dotnet run --no-build -- started-fixed
200 from /api/v1/patients: Maria Garcia, David Chen
200 from /api/v1/patients: Maria Garcia, David Chen

Both properties are set in the initializer, before any request, and the same instance then sends two requests without complaint.

How it was reproduced

A console app (dotnet new console, no packages) on .NET SDK 10.0.403 called a minimal API (dotnet new web) running on http://127.0.0.1:15801 with a GET route at /api/v1/patients. The failing run set BaseAddress, made one successful GET, then assigned Timeout; the fixed run set both in the initializer and made two GETs. The stack trace of the failing run points at the Timeout assignment (set_Timeout, line 40 of Program.cs), not at the GET before it, which had already returned its 200 answer from the test server.

Frequently asked

How do I change HttpClient Timeout after the first request?
Not on the same instance: in the test the setter threw InvalidOperationException after one request. Set Timeout when the client is created, in the object initializer.
Why did my HttpClient code work once and then throw 'already started one or more requests'?
In the test the sequence was: set BaseAddress, make one GET (it returned 200), set Timeout, and that last assignment threw. A property set before the first request was accepted; the one set after it was refused.
Can I set BaseAddress on HttpClient before the first request?
Yes. In the test, BaseAddress was assigned after the constructor and before the first GET, and the GET returned 200.

More decoded errors in the Fixes category. When the timeout you set does fire, the exception is a different one: TaskCanceledException from HttpClient.Timeout.