{"record":{"id":"558f33d799b74a5c","repo":"OpenRA/OpenRA","slug":"widget-type-gettype-name-is-not-cloneable","errorCode":null,"errorMessage":"Widget type `{GetType().Name}` is not cloneable.","messagePattern":"Widget type `(.+?)` is not cloneable\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"OpenRA.Game/Widgets/Widget.cs","lineNumber":269,"sourceCode":"\t\t\tLogic = widget.Logic;\n\t\t\tVisible = widget.Visible;\n\n\t\t\tBounds = widget.Bounds;\n\t\t\tParent = widget.Parent;\n\n\t\t\tIsVisible = widget.IsVisible;\n\t\t\tIgnoreChildMouseOver = widget.IgnoreChildMouseOver;\n\t\t\tIgnoreMouseOver = widget.IgnoreMouseOver;\n\n\t\t\tdefaultCursor = widget.defaultCursor;\n\n\t\t\tforeach (var child in widget.Children)\n\t\t\t\tAddChild(child.Clone());\n\t\t}\n\n\t\tpublic virtual Widget Clone()\n\t\t{\n\t\t\tthrow new InvalidOperationException($\"Widget type `{GetType().Name}` is not cloneable.\");\n\t\t}\n\n\t\tpublic virtual int2 RenderOrigin\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\tvar offset = (Parent == null) ? int2.Zero : Parent.ChildOrigin;\n\t\t\t\treturn new int2(Bounds.X, Bounds.Y) + offset;\n\t\t\t}\n\t\t}\n\n\t\tpublic virtual int2 ChildOrigin => RenderOrigin;\n\n\t\tpublic virtual Rectangle RenderBounds\n\t\t{\n\t\t\tget\n\t\t\t{\n\t\t\t\tvar ro = RenderOrigin;","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/OpenRA/OpenRA/blob/a520984d91eda9de48a62b1d15c1e3bad0d4fb1a/OpenRA.Game/Widgets/Widget.cs#L251-L287","documentation":"Widget.Clone() is a virtual method whose base implementation always throws, signaling that the widget type does not support cloning. Subclasses that need to be cloneable must override Clone() to perform a deep copy including children. The exception names the actual runtime type via GetType().Name so the developer knows exactly which widget type is missing the override. This fires during widget duplication (e.g. template-based UI construction).","triggerScenarios":"Code calls Clone() on a Widget subclass that has not overridden the virtual Clone() method. This can happen during chrome template instantiation, widget duplication for list/repeater widgets, or any code path that deep-copies the widget tree.","commonSituations":"Creating a new custom widget type and forgetting to override Clone(), or adding a widget to a container that clones its children (like an IterableWidgetGroup or scroll list) without implementing Clone(). The error surfaces at the point of duplication, not at definition time.","solutions":["Override Clone() in the widget subclass to return a deep copy (including copying all fields and recursively cloning children).","If the widget should never be cloned, ensure it is not used in contexts that clone children (remove from template/repeater containers).","Follow the existing pattern: call the copy constructor, copy fields, and clone each child via AddChild(child.Clone())."],"exampleFix":"// before\npublic class MyWidget : Widget\n{\n    // no Clone() override — Clone() throws\n}\n\n// after\npublic class MyWidget : Widget\n{\n    public MyWidget() {}\n    public MyWidget(MyWidget other) : base(other)\n    {\n        // copy MyWidget-specific fields here\n    }\n\n    public override Widget Clone()\n    {\n        return new MyWidget(this);\n    }\n}","handlingStrategy":"validation","validationCode":"// Check if a widget type is cloneable before calling Clone\nvar cloneable = widget as ICloneable;\n// Note: Widget.Clone() always throws in the base class, so only\n// subclasses that override Clone() are safe to clone.\n// Validate by checking the type has a copy constructor:\nstatic bool IsCloneable(Widget w)\n{\n    return w.GetType().GetConstructor(new[] { w.GetType() }) != null;\n}","typeGuard":"// Check whether the widget overrides Clone before calling it\nstatic bool IsWidgetCloneable(Widget widget)\n{\n    var method = widget.GetType().GetMethod(\"Clone\");\n    return method != null && method.DeclaringType != typeof(Widget);\n}","tryCatchPattern":null,"preventionTips":["Always override Clone() (with a matching copy constructor) in custom widget types.","If a widget will never be cloned, keep it out of containers that clone children (repeaters, lists).","Follow the established pattern: copy constructor + override Clone returning new MyType(this)."],"tags":["ui","widget","clone","virtual-method"],"backgroundTag":null,"analyzedSha":"a520984d91eda9de48a62b1d15c1e3bad0d4fb1a","analyzedAt":"2026-08-13T15:30:14.787Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}