dotnet/wpf · error · ArgumentException

SR.Format(SR.TextCompositionManager_TextCompositionHasStarte…

Error message

SR.Format(SR.TextCompositionManager_TextCompositionHasStarted, "composition")

What it means

UnsafeStartComposition throws ArgumentException when the composition's Stage is already past TextCompositionStage.None, i.e. StartComposition is being called on a composition that has already been started. WPF tracks composition lifecycle to prevent duplicate start events.

Solutions

  1. Create a fresh TextComposition for each start instead of reusing one.
  2. Check composition.Stage == TextCompositionStage.None before calling StartComposition.
  3. Track whether StartComposition was already invoked for that composition instance.

Example fix

// before
TextCompositionManager.StartComposition(composition);
TextCompositionManager.StartComposition(composition); // throws
// after
if (composition.Stage == TextCompositionStage.None)
    TextCompositionManager.StartComposition(composition);
Defensive patterns

Strategy: validation

Validate before calling

if (composition.Stage != TextCompositionStage.None) throw new InvalidOperationException("Composition already started.");

Type guard

static bool CanStart(TextComposition c) => c != null && c.Stage == TextCompositionStage.None;

Try / catch

try { TextCompositionManager.StartComposition(composition); } catch (ArgumentException ex) { /* already started */ }

Prevention

When it happens

Trigger: Calling TextCompositionManager.StartComposition twice on the same TextComposition, or reusing a composition that already went through the start pipeline (via PreProcessInput/PostProcessInput).

Common situations: Caching a TextComposition and retrying StartComposition after a handler suppressed the first pass; event re-entrancy when input handlers themselves call StartComposition.

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 dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/34703300d3c426f1. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/TextCompositionManager.cs:319

        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------

        private static bool UnsafeStartComposition(TextComposition composition)
        {
            ArgumentNullException.ThrowIfNull(composition);

            if (composition._InputManager == null)
            {
                throw new ArgumentException(SR.Format(SR.TextCompositionManager_NoInputManager, "composition"));
            }

            if (composition.Stage != TextCompositionStage.None)
            {
                throw new ArgumentException(SR.Format(SR.TextCompositionManager_TextCompositionHasStarted, "composition"));
            }

            composition.Stage = TextCompositionStage.Started;
            TextCompositionEventArgs textargs = new TextCompositionEventArgs(composition._InputDevice, composition)
            {
                RoutedEvent = TextCompositionManager.PreviewTextInputStartEvent,
                Source = composition.Source
            };
            return composition._InputManager.ProcessInput(textargs);
        }

        private static bool UnsafeUpdateComposition(TextComposition composition)
        {
            ArgumentNullException.ThrowIfNull(composition);

            if (composition._InputManager == null)
            {
                throw new ArgumentException(SR.Format(SR.TextCompositionManager_NoInputManager, "composition"));

View on GitHub (pinned to 81131a70a4)