stride3d/stride · error · ArgumentException

Invalid delta on reference count. It must be non-negative…

Error message

Invalid delta on reference count. It must be non-negative after updating. Current reference count: [{resourceLink.ReferenceCount}] Delta: [{referenceDelta}]

What it means

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.

Solutions

  1. Balance every AddReference with exactly one ReleaseReference; audit call sites for extra releases.
  2. Do not release a resource whose reference count is already zero; track ownership explicitly.
  3. Use GetTemporaryResource/its disposal pattern instead of manual add/release to avoid miscounting.
  4. Serialize access (locking) if the allocator is shared across threads.

Example fix

// before
allocator.ReleaseReference(texture); // called twice by mistake
allocator.ReleaseReference(texture);
// after
using (var temp = allocator.GetTemporaryResource(texture))
{
    // use resource; released exactly once on dispose
}
Defensive patterns

Strategy: validation

Validate before calling

if (referenceCount + delta < 0)
    throw new InvalidOperationException("Unbalanced release detected");

Type guard

bool CanRelease(GraphicsResourceLink l, int delta) => l.ReferenceCount + delta >= 0;

Try / catch

try { allocator.ReleaseReference(texture); }
catch (ArgumentException ex) { log.Error("refcount underflow", ex); }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/38e77c3489072bd3. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Graphics/GraphicsResourceAllocator.cs:544

            // Resource not found in the cache
            return false;
        }

        /// <summary>
        ///   Updates the reference count for a specified Graphics Resource.
        /// </summary>
        /// <param name="resourceLink">The Graphics Resource whose reference count is to be updated.</param>
        /// <param name="referenceDelta">
        ///   The change in the reference count. Positive values increase the count, while negative values decrease it.
        /// </param>
        /// <exception cref="ArgumentException">
        ///   The <paramref name="referenceDelta"/> is invalid. It cannot make the reference count of the <paramref name="resource"/> negative.
        /// </exception>
        private void UpdateCounter(GraphicsResourceLink resourceLink, int referenceDelta)
        {
            if ((resourceLink.ReferenceCount + referenceDelta) < 0)
            {
                throw new ArgumentException("Invalid delta on reference count. It must be non-negative after updating. " +
                    $"Current reference count: [{resourceLink.ReferenceCount}] Delta: [{referenceDelta}]");
            }

            resourceLink.ReferenceCount += referenceDelta;
            resourceLink.AccessTotalCount++;
            resourceLink.AccessCountSinceLastRecycle++;
            resourceLink.LastAccessTime = DateTime.Now;

            // If no alive references are left, we can tag the resource for discarding on next Map
            if (resourceLink.ReferenceCount == 0)
                GraphicsDevice.TagResourceAsNotAlive(resourceLink);
        }
    }
}

View on GitHub (pinned to 96fad776d2)