{"record":{"id":"4a7513f1b79c1dbc","repo":"stride3d/stride","slug":"cannot-add-a-reference-for-an-object-already-released-4a7513","errorCode":null,"errorMessage":"Cannot add a reference for an object already released. AddReference/Release pair must match.","messagePattern":"Cannot add a reference for an object already released\\. AddReference/Release pair must match\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/core/Stride.Core/ReferenceBase.cs","lineNumber":19,"sourceCode":"// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)\n// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.\nnamespace Stride.Core;\n\n/// <summary>\n/// Base class for a <see cref=\"IReferencable\"/> class.\n/// </summary>\npublic abstract class ReferenceBase : IReferencable\n{\n    private int counter = 1;\n\n    /// <inheritdoc/>\n    public int ReferenceCount { get { return counter; } }\n\n    /// <inheritdoc/>\n    public virtual int AddReference()\n    {\n        var newCounter = Interlocked.Increment(ref counter);\n        if (newCounter <= 1) throw new InvalidOperationException(FrameworkResources.AddReferenceError);\n        return newCounter;\n    }\n\n    /// <inheritdoc/>\n    public virtual int Release()\n    {\n        var newCounter = Interlocked.Decrement(ref counter);\n        if (newCounter == 0)\n        {\n            try\n            {\n                Destroy();\n            }\n            finally\n            {\n                // Reverse back the counter if there are any exceptions in the destroy method\n                Interlocked.Exchange(ref counter, newCounter + 1);\n            }","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/core/Stride.Core/ReferenceBase.cs#L1-L37","documentation":"ReferenceBase.AddReference atomically increments the reference counter; a correct AddReference/Release sequence never lets the counter reach 0 while references are still being added. Interlocked.Increment returning <= 1 means the counter was already 0 or negative — the object was released — so InvalidOperationException (FrameworkResources.AddReferenceError) is thrown to flag a mismatched reference-count lifecycle.","triggerScenarios":"Calling AddReference() on an IReferencable object after its reference count already dropped to 0 (Release brought it to zero and the object was considered released).","commonSituations":"Double-Release on one code path then AddReference from another holder; caching a released GraphicsResource/texture and reusing it next frame; resurrection after a using/Dispose block; passing a disposed object across threads that already finalized its last reference.","solutions":["Fix ownership so every AddReference has exactly one matching Release (avoid Release without ownership).","Re-acquire/reload the resource instead of adding a reference to a released instance.","Audit disposal order so objects are not released while still referenced elsewhere.","Check ReferenceCount > 0 (or a Disposed flag) before calling AddReference in defensive code."],"exampleFix":"// before\ntexture.Release();\n...\ntexture.AddReference(); // throws: already released\n// after: keep it alive while you still need it\nif (texture.ReferenceCount > 0) texture.AddReference();\nelse texture = LoadTexture(name); // re-acquire","handlingStrategy":"try-catch","validationCode":"if (obj.ReferenceCount <= 0 || obj.IsDisposed)\n    throw new InvalidOperationException(\"Object already released; cannot AddReference\");","typeGuard":"static bool IsAlive(ReferenceBase r) => r.ReferenceCount > 0;","tryCatchPattern":"try { obj.AddReference(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"already released\"))\n{\n    // re-acquire the resource instead of resurrecting the released instance\n}","preventionTips":["Enforce strict AddReference/Release pairing per owner.","Never call Release on objects you do not own; never AddReference after releasing.","Clear cached references when objects are disposed so stale reuse is impossible.","Prefer using blocks/scopes for deterministic lifetimes instead of manual counting."],"tags":["invalid-operation","reference-counting","use-after-release","lifetime"],"backgroundTag":"invalid-state-transition","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}