Troubleshooting
Common issues and their solutions when working with KeenEyes.
Build Issues
"Unable to find package" during restore
Problem: dotnet restore fails with missing package errors.
Solutions:
Clear the NuGet cache and restore:
dotnet nuget locals all --clear dotnet restoreVerify
nuget.configincludes required feeds:<configuration> <packageSources> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> </packageSources> </configuration>Check for proxy issues. If behind a corporate proxy:
dotnet nuget config set http_proxy http://proxy:port
"CS0246: The type or namespace 'IComponent' could not be found"
Problem: Missing using directives or package references.
Solution: Ensure your project references KeenEyes packages:
<PackageReference Include="KeenEyes.Core" Version="1.0.0" />
And add using directives:
using KeenEyes.Core;
using KeenEyes.Abstractions;
Source generator not producing code
Problem: [Component] attributes aren't generating builder methods.
Solutions:
Ensure the type is
partial:[Component] public partial struct Position // Must be partial { public float X; public float Y; }Check generator is referenced:
<PackageReference Include="KeenEyes.Generators" Version="1.0.0" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />Rebuild the entire solution (not just the project):
dotnet build --no-incrementalCheck for generator errors in build output. Enable detailed generator diagnostics:
<PropertyGroup> <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> </PropertyGroup>Generated files appear in
obj/Debug/net10.0/generated/
Runtime Issues
"Entity does not exist" exception
Problem: Accessing a despawned entity.
Cause: Entity was despawned, but you still have a reference.
Solutions:
Check entity validity before access:
if (world.IsAlive(entity)) { ref var pos = ref world.Get<Position>(entity); }Use versioned entity handles - the version number detects stale references:
var entity = world.Spawn().Build(); // Version 1 world.Despawn(entity); // Same ID might be reused, but with Version 2 // world.IsAlive(entity) returns false because version mismatches
Query returns no entities when it should
Problem: world.Query<A, B>() returns empty even with matching entities.
Causes & Solutions:
Components not registered:
// Ensure components are registered before creating entities world.Components.Register<Position>(); world.Components.Register<Velocity>();Entity doesn't have all required components:
// This entity won't match Query<Position, Velocity>() var entity = world.Spawn() .With(new Position { X = 0, Y = 0 }) // Missing Velocity! .Build();Entity has excluded component:
// This won't match Query<Position>().Without<Disabled>() var entity = world.Spawn() .With(new Position { X = 0, Y = 0 }) .WithTag<Disabled>() // Excluded by Without<> .Build();
"Collection was modified" during iteration
Problem: Modifying entities while iterating throws an exception.
Cause: Adding/removing components or spawning/despawning during a foreach loop.
Solution: Use a command buffer:
// BAD: Modifies during iteration
foreach (var entity in world.Query<Health>())
{
if (world.Get<Health>(entity).Current <= 0)
world.Despawn(entity); // Throws!
}
// GOOD: Deferred modification
var buffer = world.GetCommandBuffer();
foreach (var entity in world.Query<Health>())
{
if (world.Get<Health>(entity).Current <= 0)
buffer.Despawn(entity); // Queued
}
buffer.Execute(); // Applied after iteration
Component data appears corrupt or stale
Problem: Reading component returns wrong values.
Causes & Solutions:
Not using ref for modification:
// BAD: Copies the struct, changes are lost var pos = world.Get<Position>(entity); pos.X = 100; // Modifies the copy! // GOOD: Reference to actual data ref var pos = ref world.Get<Position>(entity); pos.X = 100; // Modifies actual componentUsing stale entity reference:
var entity = someOldReference; // Entity may have been despawned and ID reused if (!world.IsAlive(entity)) return; // Don't access stale entity
System not executing
Problem: System's Update() method never called.
Causes & Solutions:
System not added to world:
world.AddSystem<MovementSystem>(); // Required! world.Update(deltaTime);System disabled:
var system = world.GetSystem<MovementSystem>(); system.Enabled = true; // Ensure enabledWrong phase:
// System only runs in Update phase public override SystemPhase Phase => SystemPhase.Update; // But you're calling: world.FixedUpdate(fixedDeltaTime); // Wrong phase!System order issue - check ordering:
public override int Order => 100; // Higher = runs later
Performance Issues
Slow query iteration
Problem: Queries are taking too long.
Solutions:
Avoid Query allocation in hot path:
// BAD: Creates query object each frame foreach (var e in world.Query<Position, Velocity>()) // GOOD: Cache the query private QueryDescription<Position, Velocity> movingQuery; public override void Initialize() { movingQuery = world.Query<Position, Velocity>(); } public override void Update(float dt) { foreach (var e in movingQuery) // Reuses queryUse
ref readonlyfor read-only access:// Tells compiler component won't be modified ref readonly var vel = ref world.Get<Velocity>(entity);Avoid large components:
// BAD: Large component = cache misses public struct BigComponent : IComponent { public byte[] Data; // 1KB array } // GOOD: Keep components small public struct DataRef : IComponent { public int DataIndex; // Reference to external storage }
High memory usage
Problem: World uses excessive memory.
Solutions:
Despawn unused entities:
// Don't just disable - actually remove world.Despawn(unusedEntity);Use entity pooling for frequently spawned/despawned entities: See Entity Pooling
Avoid component bloat:
// BAD: Entity has many unused components var entity = world.Spawn() .With(new Position()) .With(new Velocity()) .With(new Health()) // Not all entities need all these .With(new Inventory()) .With(new AIState()) .Build(); // GOOD: Only add what's needed var bullet = world.Spawn() .With(new Position()) .With(new Velocity()) .Build();
Frame time spikes
Problem: Occasional long frames.
Solutions:
Spread work across frames:
// BAD: Process all at once foreach (var entity in world.Query<AIState>()) UpdateAI(entity); // 1000 entities = 1000 updates // GOOD: Process in batches private int batchIndex = 0; private const int BatchSize = 100; public override void Update(float dt) { var entities = world.Query<AIState>().ToArray(); int start = batchIndex * BatchSize; int end = Math.Min(start + BatchSize, entities.Length); for (int i = start; i < end; i++) UpdateAI(entities[i]); batchIndex = (batchIndex + 1) % ((entities.Length / BatchSize) + 1); }Use parallel execution for independent systems: See Parallelism Guide
Serialization Issues
"Unknown component type" during deserialization
Problem: Loading a save file fails with unknown type error.
Cause: Component type not registered in target world.
Solution: Register all component types before loading:
// Ensure same components are registered
world.Components.Register<Position>();
world.Components.Register<Velocity>();
world.Components.Register<Health>();
// Then load
Serializer.Load(world, saveData);
Or use a serializer with type metadata:
var serializer = new JsonComponentSerializer();
// Serializer stores type names, not IDs
Entity references invalid after load
Problem: Entity fields in components point to wrong entities after loading.
Cause: Entity IDs are reassigned during load.
Solution: Use entity names or stable identifiers:
// BAD: Raw entity reference
[Component]
public struct FollowTarget
{
public Entity Target; // ID changes on load
}
// GOOD: Named reference
[Component]
public struct FollowTarget
{
public string TargetName; // Stable across save/load
}
// Resolve after load
var target = world.GetEntityByName(component.TargetName);
Native AOT Issues
"RuntimeTypeHandle" exception with AOT
Problem: Code fails when published with Native AOT.
Cause: Reflection used at runtime.
Solutions:
Use source-generated code instead of reflection
Check for these patterns:
// BAD: Runtime generic instantiation method.MakeGenericMethod(runtimeType); // BAD: Dynamic activation Activator.CreateInstance(type); // BAD: Assembly scanning assembly.GetTypes().Where(...)Enable AOT analysis during development:
<PropertyGroup> <IsAotCompatible>true</IsAotCompatible> </PropertyGroup>
See Why Native AOT? for details.
Common Mistakes
Using == for float comparison
Problem: Float comparisons fail due to precision.
// BAD: May fail due to floating-point precision
if (velocity.X == 0)
// GOOD: Use tolerance-based comparison
using KeenEyes.Common;
if (velocity.X.IsApproximatelyZero())
Forgetting to call Build()
Problem: Entity spawning doesn't work.
// BAD: Missing Build() - entity not created
world.Spawn()
.With(new Position { X = 0, Y = 0 });
// GOOD: Build() creates the entity
var entity = world.Spawn()
.With(new Position { X = 0, Y = 0 })
.Build();
Not disposing World
Problem: Memory leaks.
// BAD: Never disposed
var world = new World();
// ... use world
// world stays in memory
// GOOD: Using statement ensures disposal
using var world = new World();
// ... use world
// Automatically disposed at end of scope
Getting More Help
If your issue isn't covered here:
- Check the API Documentation for detailed method documentation
- Review Architecture Decisions for design rationale
- Search GitHub Issues
- Open a new issue with:
- KeenEyes version
- .NET version
- Minimal reproduction code
- Expected vs actual behavior