{"record":{"id":"551744d7626fd356","repo":"EllanJiang/GameFramework","slug":"object-0-spawn-count-is-less-than-0","errorCode":null,"errorMessage":"Object '{0}' spawn count is less than 0.","messagePattern":"Object '(.+?)' spawn count is less than 0\\.","errorType":"validation","errorClass":"GameFrameworkException","httpStatus":null,"severity":"error","filePath":"GameFramework/ObjectPool/ObjectPoolManager.Object.cs","lineNumber":181,"sourceCode":"            public T Spawn()\n            {\n                m_SpawnCount++;\n                m_Object.LastUseTime = DateTime.UtcNow;\n                m_Object.OnSpawn();\n                return m_Object;\n            }\n\n            /// <summary>\n            /// 回收对象。\n            /// </summary>\n            public void Unspawn()\n            {\n                m_Object.OnUnspawn();\n                m_Object.LastUseTime = DateTime.UtcNow;\n                m_SpawnCount--;\n                if (m_SpawnCount < 0)\n                {\n                    throw new GameFrameworkException(Utility.Text.Format(\"Object '{0}' spawn count is less than 0.\", Name));\n                }\n            }\n\n            /// <summary>\n            /// 释放对象。\n            /// </summary>\n            /// <param name=\"isShutdown\">是否是关闭对象池时触发。</param>\n            public void Release(bool isShutdown)\n            {\n                m_Object.Release(isShutdown);\n                ReferencePool.Release(m_Object);\n            }\n        }\n    }\n}\n","sourceCodeStart":163,"sourceCodeEnd":197,"githubUrl":"https://github.com/EllanJiang/GameFramework/blob/d0c010b05167c58e92350449d04864a91ca13fd2/GameFramework/ObjectPool/ObjectPoolManager.Object.cs#L163-L197","documentation":"Each pooled Object<T> tracks a spawn count incremented on Spawn and decremented on Unspawn. If Unspawn is called more times than Spawn, the count would go negative, indicating unbalanced bookkeeping, so it throws. This is an internal invariant check protecting the pool's reference counting.","triggerScenarios":"Calling objectPool.Unspawn(obj) on an object that was never spawned, or unspawning the same object twice; also calling Unspawn after SetLock/lock-release cycles that reset spawn state.","commonSituations":"Double-unspawn in error/cleanup paths (e.g. unspawning in both a failure handler and a finally block); unspawning objects created directly rather than via Spawn; mismatched Spawn/Unspawn after exception in between.","solutions":["Only Unspawn objects obtained from objectPool.Spawn, exactly once per Spawn","Track ownership of the spawn reference in your code; guard with CanSpawn/HasObject checks where available","Audit error paths for duplicated Unspawn calls (remove one from handler/finally)"],"exampleFix":"// before\nfinally { pool.Unspawn(obj); }\ncatch { pool.Unspawn(obj); } // unspawns twice on error\n// after\ncatch { /* log only */ }\nfinally { pool.Unspawn(obj); }","handlingStrategy":"validation","validationCode":"if (pool.HasObject(obj.Name) && /* you hold a spawn reference */ ownsSpawn) pool.Unspawn(obj);","typeGuard":"bool CanUnspawn(ObjectInfo info) => info != null && info.SpawnCount > 0;","tryCatchPattern":"try { pool.Unspawn(obj); }\ncatch (GameFrameworkException ex) { Log.Error(\"Unspawn without matching Spawn for '{0}'\", obj.Name); }","preventionTips":["Strictly pair Spawn/Unspawn, one Unspawn per Spawn","Avoid Unspawn in both catch and finally","Only unspawn objects returned by Spawn"],"tags":["objectpool","spawn-count","invariant-violation","gameframework"],"backgroundTag":"internal-invariant-violation","analyzedSha":"d0c010b05167c58e92350449d04864a91ca13fd2","analyzedAt":"2026-09-15T13:37:15.352Z","contentChangedAt":"2026-09-15T13:37:15.352Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}