{"record":{"id":"4c28ebec711894f2","repo":"dotnet/wpf","slug":"sr-format-sr-stylebasedonhasloop","errorCode":null,"errorMessage":"SR.Format(SR.StyleBasedOnHasLoop)","messagePattern":"SR\\.Format\\(SR\\.StyleBasedOnHasLoop\\)","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Style.cs","lineNumber":577,"sourceCode":"        /// <remarks>\n        /// Classic \"when did we enter the cycle\" problem where we don't know\n        ///  what to start remembering and what to check against.  Brute-\n        ///  force approach here is to remember everything with a stack\n        ///  and do a linear comparison through everything.  Since the Style\n        ///  BasedOn hierarchy is not expected to be large, this should be OK.\n        /// </remarks>\n        private void CheckForCircularBasedOnReferences()\n        {\n            Stack basedOnHierarchy = new Stack(10);  // 10 because that's the default value (see MSDN) and the perf team wants us to specify something.\n            Style latestBasedOn = this;\n\n            while( latestBasedOn != null )\n            {\n                if( basedOnHierarchy.Contains( latestBasedOn ) )\n                {\n                    // Uh-oh.  We've seen this Style before.  This means\n                    //  the BasedOn hierarchy contains a loop.\n                    throw new InvalidOperationException(SR.Format(\n                        SR.StyleBasedOnHasLoop));\n\n                    // Debugging note: If we stop here, the basedOnHierarchy\n                    //  object is still alive and we can browse through it to\n                    //  see what we've explored.  (This does not apply if\n                    //  somebody catches this exception and re-throws.)\n                }\n\n                // Haven't seen it, push on stack and go to next level.\n                basedOnHierarchy.Push( latestBasedOn );\n                latestBasedOn = latestBasedOn.BasedOn;\n            }\n\n            return;\n        }\n\n        // Iterates through the setters collection and adds the EventSetter information into\n        // an EventHandlersStore for easy and fast retrieval during event routing. Also adds","sourceCodeStart":559,"sourceCodeEnd":595,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Style.cs#L559-L595","documentation":"CheckForCircularBasedOnReferences walks the BasedOn chain during Seal and detects a style that appears twice in the hierarchy, i.e. a cycle (A BasedOn B BasedOn ... BasedOn A). Cycles would cause infinite recursion during style resolution, so Seal throws InvalidOperationException (SR.StyleBasedOnHasLoop).","triggerScenarios":"Setting styleA.BasedOn = styleB and later styleB.BasedOn = styleA (directly or through a longer chain) before either is sealed; programmatically rewiring BasedOn in a loop over styles.","commonSituations":"Code-generated style graphs where parents are assigned in dependency order incorrectly; late mutation of BasedOn on cached styles creating a back-reference; merging dictionaries where two styles reference each other.","solutions":["Break the cycle: make one style's BasedOn null or point it at a genuinely separate base style.","Construct style hierarchies bottom-up (base styles first) and never re-parent a style that is already an ancestor.","Add an assertion/loop check in code that assigns BasedOn dynamically."],"exampleFix":"// before\nstyleA.BasedOn = styleB;\nstyleB.BasedOn = styleA; // cycle\n// after\nstyleA.BasedOn = null;\nstyleB.BasedOn = styleA;","handlingStrategy":"validation","validationCode":"var seen = new HashSet<Style>();\nfor (var s = style.BasedOn; s != null; s = s.BasedOn)\n    if (!seen.Add(s)) throw new InvalidOperationException(\"BasedOn cycle detected before Seal.\");","typeGuard":"static bool HasBasedOnLoop(Style s) { var seen = new HashSet<Style>(); for (var b = s.BasedOn; b != null; b = b.BasedOn) if (!seen.Add(b)) return true; return false; }","tryCatchPattern":"try { style.Seal(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"loop\")) { log.Error(\"Circular BasedOn\", ex); }","preventionTips":["Build style hierarchies bottom-up and never re-parent ancestors.","Track assignments of BasedOn in code-generated style graphs.","Walk the BasedOn chain before sealing to detect cycles early."],"tags":["wpf","style","basedon","circular-reference"],"backgroundTag":"circular-reference","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}