dotnet/wpf · error · XpsSerializationException

SR.ReachSerialization_CycleDetectedInSerialization

Error message

SR.ReachSerialization_CycleDetectedInSerialization

What it means

ReachObjectContext.CreateContext throws XpsSerializationException (ReachSerialization_CycleDetectedInSerialization) when it detects that the object about to get a new serialization context is already being serialized higher up the context stack (its TargetObject matches a current context). Serializing a cyclic object graph would loop forever or overflow the stack, so the XPS serializer fails fast. XPS documents must be tree-shaped; cycles are unsupported.

Solutions

  1. Break the cycle by making the graph a strict tree before serialization (remove back-references from the serialized tree)
  2. Clone shared sub-objects so each appears only once in the graph
  3. Skip serializing parent references (mark them [SerializeIgnore]-like by rebuilding a serializable copy)
  4. Catch XpsSerializationException and walk the graph for repeated references before saving

Example fix

// before
child.Parent = parent;   // cycle: parent.Children contains child
writer.Write(parent);
// after
var copy = CloneForSerialization(parent); // drop Parent back-reference
writer.Write(copy);
Defensive patterns

Strategy: validation

Validate before calling

static bool HasCycle(object root) { var seen = new HashSet<object>(); var stack = new Stack<object>(); stack.Push(root); while (stack.Count > 0) { var o = stack.Pop(); if (o == null || !seen.Add(o)) continue; foreach (var c in GetChildren(o)) stack.Push(c); } return false; } // GetChildren enumerates visual/logical children; a repeated node indicates a cycle

Type guard

static bool IsAcyclic(object root) => !ContainsReference(root, root, new HashSet<object>());

Try / catch

try { writer.Write(root); } catch (System.Windows.Xps.XpsSerializationException ex) when (ex.Message.Contains("cycle")) { log.Error("Cyclic object graph passed to XPS writer", ex); throw; }

Prevention

When it happens

Trigger: Calling the XPS serializer on an object graph containing a reference cycle — e.g. a Visual whose Children collection (transitively) contains an ancestor, or parent/child properties pointing at each other.

Common situations: Reusing the same Visual/FixedPage instance in multiple places in a document; models with bidirectional parent-child references being fed straight into XpsDocumentWriter; shared resources attached into the visual tree more than once.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/bb9710e8cfdbd82d. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/ReachFramework/Serialization/manager/ReachObjectContext.cs:132

        {
            //
            // Check for element pre-existance to avoid infinite loops
            // in the process of serialization
            //
            int stackIndex = 0;

            object currentObject = null;

            for(currentObject = serializationManager.GraphContextStack[stackIndex];
                currentObject != null;
                currentObject = serializationManager.GraphContextStack[++stackIndex])
            {
                SerializableObjectContext currentObjectContext = currentObject as SerializableObjectContext;

                if(currentObjectContext!=null &&
                   currentObjectContext.TargetObject == serializableObject)
                {
                    throw new XpsSerializationException(SR.ReachSerialization_CycleDetectedInSerialization);
                }
            }

            SerializableObjectContext serializableObjectContext;
            lock (_stackLock)
            {
                serializableObjectContext =
                    _recycableSerializableObjectContexts.Count == 0 ?
                    null :
                    (SerializableObjectContext)_recycableSerializableObjectContexts.Pop();
            }

            if(serializableObjectContext == null)
            {
                serializableObjectContext = new SerializableObjectContext(serializableObject,
                                                                          serializablePropertyContext);
            }
            else

View on GitHub (pinned to 81131a70a4)