{"record":{"id":"38e77c3489072bd3","repo":"stride3d/stride","slug":"invalid-delta-on-reference-count-it-must-be-non-negative","errorCode":null,"errorMessage":"Invalid delta on reference count. It must be non-negative after updating. Current reference count: [{resourceLink.ReferenceCount}] Delta: [{referenceDelta}]","messagePattern":"Invalid delta on reference count\\. It must be non-negative after updating\\. Current reference count: \\[(.+?)\\] Delta: \\[(.+?)\\]","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.Graphics/GraphicsResourceAllocator.cs","lineNumber":544,"sourceCode":"            // Resource not found in the cache\n            return false;\n        }\n\n        /// <summary>\n        ///   Updates the reference count for a specified Graphics Resource.\n        /// </summary>\n        /// <param name=\"resourceLink\">The Graphics Resource whose reference count is to be updated.</param>\n        /// <param name=\"referenceDelta\">\n        ///   The change in the reference count. Positive values increase the count, while negative values decrease it.\n        /// </param>\n        /// <exception cref=\"ArgumentException\">\n        ///   The <paramref name=\"referenceDelta\"/> is invalid. It cannot make the reference count of the <paramref name=\"resource\"/> negative.\n        /// </exception>\n        private void UpdateCounter(GraphicsResourceLink resourceLink, int referenceDelta)\n        {\n            if ((resourceLink.ReferenceCount + referenceDelta) < 0)\n            {\n                throw new ArgumentException(\"Invalid delta on reference count. It must be non-negative after updating. \" +\n                    $\"Current reference count: [{resourceLink.ReferenceCount}] Delta: [{referenceDelta}]\");\n            }\n\n            resourceLink.ReferenceCount += referenceDelta;\n            resourceLink.AccessTotalCount++;\n            resourceLink.AccessCountSinceLastRecycle++;\n            resourceLink.LastAccessTime = DateTime.Now;\n\n            // If no alive references are left, we can tag the resource for discarding on next Map\n            if (resourceLink.ReferenceCount == 0)\n                GraphicsDevice.TagResourceAsNotAlive(resourceLink);\n        }\n    }\n}\n","sourceCodeStart":526,"sourceCodeEnd":559,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.Graphics/GraphicsResourceAllocator.cs#L526-L559","documentation":"UpdateCounter validates that applying referenceDelta to the resource link's current ReferenceCount never yields a negative count; if it would, ArgumentException is thrown with the current count and delta in the message. This guards against unbalanced ReleaseReference calls or corrupt bookkeeping in GetTemporaryResource/UpdateReferenceCount.","triggerScenarios":"Releasing a resource more times than it was referenced (delta such that ReferenceCount + delta < 0), e.g. double ReleaseReference, or calling ReleaseReference on a temporary resource whose count already reached 0.","commonSituations":"Asymmetric Add/Release calls across code paths or exception handlers; reusing a stale resource handle after recycling reset its count; concurrency bugs where two threads release the same resource.","solutions":["Balance every AddReference with exactly one ReleaseReference; audit call sites for extra releases.","Do not release a resource whose reference count is already zero; track ownership explicitly.","Use GetTemporaryResource/its disposal pattern instead of manual add/release to avoid miscounting.","Serialize access (locking) if the allocator is shared across threads."],"exampleFix":"// before\nallocator.ReleaseReference(texture); // called twice by mistake\nallocator.ReleaseReference(texture);\n// after\nusing (var temp = allocator.GetTemporaryResource(texture))\n{\n    // use resource; released exactly once on dispose\n}","handlingStrategy":"validation","validationCode":"if (referenceCount + delta < 0)\n    throw new InvalidOperationException(\"Unbalanced release detected\");","typeGuard":"bool CanRelease(GraphicsResourceLink l, int delta) => l.ReferenceCount + delta >= 0;","tryCatchPattern":"try { allocator.ReleaseReference(texture); }\ncatch (ArgumentException ex) { log.Error(\"refcount underflow\", ex); }","preventionTips":["Pair every AddReference with exactly one ReleaseReference","Prefer GetTemporaryResource with using/dispose","Guard shared allocators with locks"],"tags":["csharp","graphics","refcount"],"backgroundTag":"invalid-argument-value","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}