stride3d/stride · error · InvalidOperationException
This TransformComponent already has a Parent, detach it…
Error message
This TransformComponent already has a Parent, detach it first.
What it means
Thrown by TransformComponent.Children collection's OnTransformAdded hook when a transform that still has a Parent is inserted into another transform's children. A TransformComponent can only belong to one parent at a time; the caller must detach it first. This keeps the transform hierarchy single-parented so world matrices stay well defined.
Solutions
- Detach first: call transform.Children.Remove(item) on the current parent, or set the old parent's collection to drop it, before adding to the new parent.
- Check item.Parent == null before adding; if not, remove from the current parent.
- Prefer entity-level APIs (Entity transformation/parent assignment) that handle detach/add atomically instead of touching TransformComponent.Children directly.
- When reusing pooled hierarchies, reset parent links (item.parent) before re-attaching.
Example fix
// before
newParent.Transform.Children.Add(child.Transform); // throws if still parented
// after
if (child.Transform.Parent != null)
child.Transform.Parent.Children.Remove(child.Transform);
newParent.Transform.Children.Add(child.Transform); Defensive patterns
Strategy: validation
Validate before calling
bool canAttach(TransformComponent newParent, TransformComponent child) => child.Parent == null && newParent != child;
Type guard
bool IsDetached(TransformComponent t) => t?.Parent == null;
Try / catch
try { parentTransform.Children.Add(childTransform); } catch (InvalidOperationException ex) { log.Error($"{childTransform.Entity?.Name} still parented: {ex.Message}"); } Prevention
- Always detach (Remove from current parent) before re-parenting
- Guard with child.Parent == null check before Add
- Prefer entity-level parenting APIs over raw TransformComponent.Children edits
- Reset pooled hierarchies' parent links on reuse
When it happens
Trigger: Calling transform.Children.Add(otherTransform) (or an indexer insert/set that routes through InsertItem/SetItem) while otherTransform.Parent != null — e.g. re-parenting an entity's TransformComponent without removing it from its previous parent's children.
Common situations: Runtime entity re-parenting scripts that forget to detach; dragging entities between parents in the editor via custom tooling; reusing pooled entity hierarchies whose transforms were not reset.
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
- Custom strides is not supported with packed PixelFormats
- This TransformComponent's parent is not the expected value.
- A region can only be specified for Textures with…
- A texture with size [ ] already exist with the same output…
- A UIPage asset can't have more than one root.
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/55d87de15683cb4f.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.Engine/Engine/TransformComponent.cs:357
transformOperation.Process(this);
}
}
[DataContract]
public class TransformChildrenCollection : FastCollection<TransformComponent>
{
TransformComponent transform;
Entity Entity => transform.Entity;
public TransformChildrenCollection(TransformComponent transformParam)
{
transform = transformParam;
}
private void OnTransformAdded(TransformComponent item)
{
if (item.Parent != null)
throw new InvalidOperationException("This TransformComponent already has a Parent, detach it first.");
item.parent = transform;
Entity?.EntityManager?.OnHierarchyChanged(item.Entity);
Entity?.EntityManager?.GetProcessor<TransformProcessor>().NotifyChildrenCollectionChanged(item, true);
}
private void OnTransformRemoved(TransformComponent item)
{
if (item.Parent != transform)
throw new InvalidOperationException("This TransformComponent's parent is not the expected value.");
item.parent = null;
Entity?.EntityManager?.OnHierarchyChanged(item.Entity);
Entity?.EntityManager?.GetProcessor<TransformProcessor>().NotifyChildrenCollectionChanged(item, false);
}
/// <inheritdoc/>View on GitHub (pinned to 96fad776d2)