microsoft/semantic-kernel · error · InvalidOperationException
Application is already stopped.
Error message
Application is already stopped.
What it means
AgentsApp.ShutdownAsync uses Interlocked.Exchange to flip _runningCount from 1 to 0; if it was not 1 (i.e. the app is not running) it throws InvalidOperationException. This prevents stopping a host that was never started or was already stopped.
Source
Thrown at dotnet/src/Agents/Runtime/Core/AgentsApp.cs:70
public async ValueTask StartAsync()
{
if (Interlocked.Exchange(ref this._runningCount, 1) != 0)
{
throw new InvalidOperationException("Application is already running.");
}
await this.Host.StartAsync().ConfigureAwait(false);
}
/// <summary>
/// Shuts down the application by stopping the host.
/// Throws an exception if the application is not running.
/// </summary>
public async ValueTask ShutdownAsync()
{
if (Interlocked.Exchange(ref this._runningCount, 0) != 1)
{
throw new InvalidOperationException("Application is already stopped.");
}
await this.Host.StopAsync().ConfigureAwait(false);
}
/// <summary>
/// Publishes a message to the specified topic.
/// If the application is not running, it starts the host first.
/// </summary>
/// <typeparam name="TMessage">The type of the message being published.</typeparam>
/// <param name="message">The message to publish.</param>
/// <param name="topic">The topic to which the message will be published.</param>
/// <param name="messageId">An optional unique identifier for the message.</param>
/// <param name="cancellationToken">A token to cancel the operation if needed.</param>
public async ValueTask PublishMessageAsync<TMessage>(TMessage message, TopicId topic, string? messageId = null, CancellationToken cancellationToken = default)
where TMessage : notnull
{
if (Volatile.Read(ref this._runningCount) == 0)View on GitHub (pinned to c028a0c7dc)
Solutions
- Only call ShutdownAsync if StartAsync (or an auto-starting publish) succeeded.
- Track started state yourself and skip shutdown when not running.
- Catch and swallow InvalidOperationException in best-effort shutdown paths if appropriate.
Example fix
// before
await app.StartAsync();
// ... work ...
await app.ShutdownAsync();
await app.ShutdownAsync(); // throws
// after
await app.StartAsync();
bool started = true;
try { /* work */ }
finally { if (started) await app.ShutdownAsync(); } Defensive patterns
Strategy: validation
Validate before calling
bool started = false;
try { await app.StartAsync(); started = true; }
catch (InvalidOperationException) { /* was already running */ started = true; }
// later:
if (started) await app.ShutdownAsync(); Try / catch
try { await app.ShutdownAsync(); }
catch (InvalidOperationException) { /* not running; nothing to stop */ } Prevention
- Only shut down when a prior successful start occurred.
- Drive teardown from a single ownership path.
- In best-effort cleanup, swallow the 'already stopped' exception.
When it happens
Trigger: Calling ShutdownAsync without a prior StartAsync; calling ShutdownAsync twice; calling ShutdownAsync after PublishMessageAsync never actually transitioned the app to running (e.g. it threw).
Common situations: Cleanup/dispose handlers that shut down unconditionally even when startup failed; double shutdown in finally blocks across concurrent paths; tests reusing an app instance.
Related errors
- Application is already running.
- Runtime is already running.
- Runtime is already stopping.
- Invalid AgentId key: '{key}'. Must only contain ASCII charac
- Invalid AgentId type: '{type}'. Must be alphanumeric (a-z, 0
AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13).
Data as JSON: /api/errors/59268e8c4d98a279.
Report an issue: GitHub.