"AppendRemovedComponentRecordError" when instantiating an Entity in Unity DOTS

I’m trying to use Unity DOTS for a game project, however I keep hitting the following error mentionned above when trying to instantiate Entities referenced in a BlobArray:

ArgumentException: System.ArgumentException: System.String Unity.Entities.EntityComponentStore::AppendRemovedComponentRecordError(Unity.Entities.Entity,Unity.Entities.ComponentType)
This Exception was thrown from a function compiled with Burst, which has limited exception support.

My logic up until now is to create a BlobArray of InstantiatableEntityData, which contains two things: The hash of a string that would function like an ID, and an Entity. Each time my BuildingScreenSystem receives an input, it reads an ID and compares it to its BlobArray to see which Entity is currently requested; It then instantiates this Entity. It’s during this last part that it throws the error mentionned in the title.

Here’s are the two relevant files:

GameAuthoring.cs, which creates the singleton component Game, containing the BlobAssetReference.

public class GameAuthoring : MonoBehaviour
{
   [SerializeField] private List<InstantiatableEntity> instantiatableEntities;
  
   private class Baker : Baker<GameAuthoring>
   {
       public override void Bake(GameAuthoring authoring)
       {
           var entity = GetEntity(TransformUsageFlags.None);

           AddComponent(entity, new Game()
           {
               State = GameState.Starting,
               EntityPool = CreateEntityPool(authoring.instantiatableEntities)
           });
       }
      
       private BlobAssetReference<InstantiatableEntityPool> CreateEntityPool(List<InstantiatableEntity> instantiatableEntities)
       {
           var builder = new BlobBuilder(Allocator.Temp);
           ref InstantiatableEntityPool instantiatableEntityPool = ref builder.ConstructRoot<InstantiatableEntityPool>();

           // Allocate enough room the entities in the pool. Use the returned BlobBuilderArray
           // to fill in the data.
           var entitiesCount = instantiatableEntities.Count;
           BlobBuilderArray<InstantiatableEntityData> arrayBuilder = builder.Allocate(
               ref instantiatableEntityPool.Entities,
               entitiesCount
           );

           for (int i = 0; i < entitiesCount; ++i)
           {
               var entry = instantiatableEntities[i];
               arrayBuilder[i] = new InstantiatableEntityData()
               {
                   EntityID = entry.entityID.GetHashCode(),
                   Entity = GetEntity(entry.entity, TransformUsageFlags.Dynamic)
               };
           }

           var result = builder.CreateBlobAssetReference<InstantiatableEntityPool>(Allocator.Persistent);
           builder.Dispose();
           return result;
       }
   }

  
}

public struct Game : IComponentData
{
   public GameState State;
   public BlobAssetReference<InstantiatableEntityPool> EntityPool;
}

[Serializable]
public struct InstantiatableEntity
{
   public string entityID;
   public GameObject entity;
}

public struct InstantiatableEntityData
{
   public int EntityID;
   public Entity Entity;
}

public struct InstantiatableEntityPool
{
   public BlobArray<InstantiatableEntityData> Entities;
}

And BuildingScreenSystem.cs, the first system using this array to instantiate a given unit at a fixed point.

partial struct BuildingScreenSystem : ISystem
{
   [BurstCompile]
   public void OnCreate(ref SystemState state)
   {
       state.RequireForUpdate<Game>();
   }
  
   // Accessing BuildingScreen, can't use BurstCompile
   public void OnUpdate(ref SystemState state)
   {  
       if (!Input.GetMouseButtonDown(0))
           return;

       var ecb = new EntityCommandBuffer(Allocator.Temp);

       var selectedID = BuildingScreenSingleton.Instance.SelectedPrefabID;
       ref var entityPool = ref SystemAPI.GetSingleton<Game>().EntityPool.Value;
       for (int i = 0; i < entityPool.Entities.Length; ++i)
       {
           Debug.Log($"{entityPool.Entities[i].Entity}");
           if (entityPool.Entities[i].EntityID == selectedID)
           {
               Debug.Log("Found match! Spawning now...");
               var newEntity = ecb.Instantiate(entityPool.Entities[i].Entity);
               ecb.SetComponent(newEntity, new LocalTransform()
               {
                   Position = new float3(0, 10, 0),
                   Rotation = quaternion.identity,
                   Scale = 1f
               });
           }
       }

       ecb.Playback(state.EntityManager);
       ecb.Dispose();
   }
}

I know the code successfully matches the two IDs thanks to my second log, but I can’t find the referenced entity given to me by the first log in the Entities Hierarchy.

So I think my error probably comes from misunderstanding how BlobAssets work, but I still can’t find any reference or explanation to this error online, so I feel like I’ve hit a wall…

Does anyone more knowledgable than me in DOTS see the issue in this code, or recognize the error?

Blobs shouldn’t store Entity values anywhere because blobs are treated as immutable, and thus Entity values are not patched to the actualized Entity instances from a loaded subscene. You would best approach this by instead using an additional buffer component type to store the array.

were you able to make any progress here?

As Spy-Master said, BlobAsset is just the wrong tool for this purpose. Use DynamicBuffer instead.

I was hoping there was a root cause to the exception, as I get it in two of my systems with literally no clues why and am not using BlobAsset anywhere in my codebase.

I can confirm my use case eventually worked after refactoring the code to use DynamicBuffers instead, as Spy-Master recommended…
This exact error is pretty frequent though, and from my understanding, it might mean you’re trying to access an Entity that no longer exists?

I suspect it’s related to setting a component on a destroyed entity from a command buffer, but I have no idea how to determine which job enqueued the command, what kind of entity is being updated, etc. The issue also crops up non-deterministically which leads me to suspect it’s a system ordering issue, which doesn’t make sense in my case.

I’ve tried recording commands using journalling but there’s simply too much noise. If I could just get the index/version for the problem entity, I’m at a loss right now.

It’s not a destroyed entity, but an entity that doesn’t exist at runtime. Because blob asset is immutable, when a subscene is loaded it cannot remap baked entities into runtime entities. The root cause is just that Unity chose to differentiate them. Maybe people who is more knowledgable about the ins and outs of Entities can give more insights about the cause.