stride3d/stride · error · InvalidOperationException
This TransformComponent's parent is not the expected value.
Error message
This TransformComponent's parent is not the expected value.
What it means
Thrown by TransformComponent.Children's OnTransformRemoved hook when removing a transform whose Parent does not match the transform that owns the collection. Mirrors the add-side check: the collection only removes transforms it actually parents. Hitting it means the child was reparented or its parent pointer changed out from under the collection.
Solutions
- Guard with if (item.Parent == transform) before calling Remove; skip or log otherwise.
- Remove the child from the parent that currently owns it (query item.Parent.Children.Remove(item)).
- Avoid duplicated detach logic across code paths; centralize re-parenting in one helper that detaches once.
- After a failed hierarchy operation, re-read Parent before retrying removal rather than assuming the old structure.
Example fix
// before
parent.Transform.Children.Remove(child.Transform); // throws if child.Parent != parent
// after
if (child.Transform.Parent == parent.Transform)
parent.Transform.Children.Remove(child.Transform); Defensive patterns
Strategy: validation
Validate before calling
bool canDetach(TransformComponent parent, TransformComponent child) => child.Parent == parent;
Type guard
bool IsOwnedBy(TransformComponent child, TransformComponent parent) => ReferenceEquals(child?.Parent, parent);
Try / catch
try { parentTransform.Children.Remove(childTransform); } catch (InvalidOperationException ex) { log.Warn($"Child parent mismatch: {ex.Message}"); } Prevention
- Check child.Parent == parent before Remove/Clear
- Avoid double-detach paths; make detach idempotent with a guard
- Track hierarchy ownership centrally to prevent stale references
- After failures, re-read Parent before retrying
When it happens
Trigger: Calling transform.Children.Remove(item) / Clear / SetItem when item.Parent != transform — e.g. item was already detached and re-added elsewhere, or item.parent was nulled by a prior remove that threw mid-way.
Common situations: Double-detach in cleanup code (removing a child twice or from two different parents); scripts that set parent via a different collection then call Remove on the stale one; exception recovery paths leaving half-detached hierarchies.
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
- Cannot add a same component multiple times. Already set at…
- Cannot add an entity to this entity manager when it is…
- Cannot run this instance while it is already running
- Custom strides is not supported with packed PixelFormats
- Entity shouldn't have a parent.
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/a9d2a1206e6f233d.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.Engine/Engine/TransformComponent.cs:367
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/>
protected override void InsertItem(int index, TransformComponent item)
{
base.InsertItem(index, item);
OnTransformAdded(item);
}
/// <inheritdoc/>
protected override void RemoveItem(int index)
{
OnTransformRemoved(this[index]);View on GitHub (pinned to 96fad776d2)