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

  1. Check TerminalAnnotation.IsInitialized before calling Initialize and skip if already true.
  2. Ensure only one pipeline stage initializes the annotation (let the framework's BuildModel/CreateHarness do it).
  3. Build a fresh ApplicationModel (re-run the distributed application builder) instead of reusing an already-initialized model.
  4. 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

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


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)