microsoft/aspire · error · InvalidOperationException
TerminalAnnotation has already been initialized.
Error message
TerminalAnnotation has already been initialized.
What it means
TerminalAnnotation is a singleton-style application-model annotation that can only be initialized once with its set of TerminalHostResources. Initialize throws InvalidOperationException if IsInitialized is already true, because re-initializing would replace an already-materialized terminal host set. The listed callers (MaterializeTerminalHostsAsync, AddSyntheticTerminalAnnotation, BuildModel, CreateHarness) are the internal paths that initialize it during app model construction/publish.
Solutions
- Check TerminalAnnotation.IsInitialized before calling Initialize and skip if already true.
- Ensure only one pipeline stage initializes the annotation (let the framework's BuildModel/CreateHarness do it).
- Build a fresh ApplicationModel (re-run the distributed application builder) instead of reusing an already-initialized model.
- Catch InvalidOperationException if double-init is an expected benign race in custom tooling.
Example fix
// before
terminalAnnotation.Initialize(terminalHosts);
// after
if (!terminalAnnotation.IsInitialized)
{
terminalAnnotation.Initialize(terminalHosts);
} Defensive patterns
Strategy: validation
Validate before calling
if (terminalAnnotation.IsInitialized)
return; // skip redundant initialization Try / catch
try
{
terminalAnnotation.Initialize(terminalHosts);
}
catch (InvalidOperationException ex) when (terminalAnnotation.IsInitialized)
{
// Already initialized by another pipeline stage; safe to ignore.
} Prevention
- Initialize TerminalAnnotation exactly once, ideally in the framework's model-build path.
- Check IsInitialized before any manual Initialize call in custom tooling or tests.
- Build a fresh ApplicationModel per pipeline run instead of reusing an initialized model.
When it happens
Trigger: Calling TerminalAnnotation.Initialize twice on the same annotation instance — e.g. a custom publish step or harness that invokes initialization after the framework (BuildModel/CreateHarness) already did, or materializing terminal hosts in both publish and run paths on a shared model.
Common situations: Custom dashboard/terminal tooling that builds the model more than once; tests creating a harness then manually initializing; re-running a distributed application builder's publish pipeline on the same ApplicationModel instance.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Already connected to
- At least one terminal host is required.
- Azure environment resource required by AKS environment
- AzureEnvironmentResource must be present in the application…
- AzureEnvironmentResource must be present in the application…
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/59b44c5c0f516f3f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting/ApplicationModel/TerminalAnnotation.cs:70
/// Gets a value indicating whether <see cref="Initialize"/> has been called yet.
/// Production code initializes during <see cref="BeforeStartEvent"/>; tests that
/// inspect <see cref="TerminalHosts"/> need to publish that event manually first.
/// </summary>
public bool IsInitialized { get; private set; }
/// <summary>
/// Populates <see cref="TerminalHosts"/> exactly once. Called by the
/// <see cref="BeforeStartEvent"/> subscriber installed by
/// <see cref="TerminalResourceBuilderExtensions.WithTerminal{T}(IResourceBuilder{T}, Action{TerminalOptions}?)"/>.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when called more than once.</exception>
internal void Initialize(IReadOnlyList<TerminalHostResource> terminalHosts)
{
ArgumentNullException.ThrowIfNull(terminalHosts);
if (IsInitialized)
{
throw new InvalidOperationException("TerminalAnnotation has already been initialized.");
}
if (terminalHosts.Count == 0)
{
throw new ArgumentException("At least one terminal host is required.", nameof(terminalHosts));
}
for (var i = 0; i < terminalHosts.Count; i++)
{
if (terminalHosts[i] is null)
{
throw new ArgumentException($"Terminal host at index {i} is null.", nameof(terminalHosts));
}
}
_terminalHosts = terminalHosts;
IsInitialized = true;
}View on GitHub (pinned to 25830f84bd)