dotnet/wpf · error · InvalidOperationException

SR.XamlMarkupExtensionWriterInputInvalid

Error message

SR.XamlMarkupExtensionWriterInputInvalid

What it means

WriteEndObject pops a node from the internal node stack to match the end object against its start object. If the stack is empty there is no matching StartObject, meaning the writer received an EndObject without a corresponding StartObject — an invalid XAML node stream. The library throws InvalidOperationException with XamlMarkupExtensionWriterInputInvalid.

Solutions

  1. Ensure every WriteEndObject is paired with exactly one prior WriteStartObject for the same object
  2. Validate/normalize the XAML node stream before forwarding it (e.g. with a custom XamlWriter that checks balance)
  3. Wrap the writing pass in try/catch for InvalidOperationException and log the offending node sequence

Example fix

// before
if (nodeType == XamlNodeType.EndObject) writer.WriteEndObject(); // no state check
// after
if (nodeType == XamlNodeType.EndObject && startObjectDepth > 0) { startObjectDepth--; writer.WriteEndObject(); }
Defensive patterns

Strategy: validation

Validate before calling

if (startObjectDepth <= 0) throw new InvalidOperationException("WriteEndObject called without an open StartObject");

Type guard

bool CanEndObject => startObjectDepth > 0;

Try / catch

try { writer.WriteEndObject(); }
catch (InvalidOperationException ex) when (ex.Message == SR.XamlMarkupExtensionWriterInputInvalid || ex.Message.Contains("markup extension")) { /* fix stream pairing */ }

Prevention

When it happens

Trigger: Calling XamlMarkupExtensionWriter.WriteEndObject() (or forwarding an EndObject node) before any WriteStartElement/WriteStartObject for the object has been issued, or calling it more times than StartObject was called.

Common situations: Hand-written node-stream emission that is off by one; a node-stream filter that drops StartObject nodes but forwards EndObject; replaying a truncated XAML node stream.

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/32cdd3d6cba1f7a5. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlMarkupExtensionWriter.cs:337

                writer.nodes.Push(new Node { NodeType = XamlNodeType.StartObject, XamlType = type });
                writer.currentState = InObjectBeforeMember.State;
            }
        }

        private abstract class InObject : WriterState
        {
            protected InObject() { }

            public abstract string Delimiter
            {
                get;
            }

            public override void WriteEndObject(XamlMarkupExtensionWriter writer)
            {
                if (writer.nodes.Count == 0)
                {
                    throw new InvalidOperationException(SR.XamlMarkupExtensionWriterInputInvalid);
                }

                Node node = writer.nodes.Pop();

                if (node.NodeType != XamlNodeType.StartObject)
                {
                    throw new InvalidOperationException(SR.XamlMarkupExtensionWriterInputInvalid);
                }

                writer.sb.Append('}');

                if (writer.nodes.Count == 0)
                {
                    writer.currentState = Start.State;
                }
                else
                {
                    Node member = writer.nodes.Peek();

View on GitHub (pinned to 81131a70a4)