abpframework/abp · error · AbpException

There is no active UOW.

Error message

There is no active UOW.

What it means

Thrown by DistributedCache<TCacheKey,TCacheItem>.GetUnitOfWorkCache() when IUnitOfWorkManager.Current is null. This method accumulates cache writes inside the current unit of work so they are committed/rolled back atomically. Calling it outside a UOW scope is a contract violation; the public cache API is expected to gate on ShouldConsiderUow first.

Source

Thrown at framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs:1565

            .Select(key => new KeyValuePair<TCacheKey, TCacheItem?>(key, default))
            .ToArray();
    }

    protected virtual bool ShouldConsiderUow(bool considerUow)
    {
        return considerUow && UnitOfWorkManager.Current != null;
    }

    protected virtual string GetUnitOfWorkCacheKey()
    {
        return UowCacheName + CacheName;
    }

    protected virtual Dictionary<TCacheKey, UnitOfWorkCacheItem<TCacheItem>> GetUnitOfWorkCache()
    {
        if (UnitOfWorkManager.Current == null)
        {
            throw new AbpException($"There is no active UOW.");
        }

        return UnitOfWorkManager.Current.GetOrAddItem(GetUnitOfWorkCacheKey(),
            key => new Dictionary<TCacheKey, UnitOfWorkCacheItem<TCacheItem>>());
    }
}

View on GitHub (pinned to 7ed43b1931)

Solutions

  1. Wrap the cache usage in a UOW: await using (var uow = UnitOfWorkManager.Begin(requiresNew: true)) { ... await cache.SetAsync(...); await uow.CompleteAsync(); }
  2. Pass considerUow: false when you intentionally operate outside a UOW.
  3. Ensure the calling service is registered correctly and runs within a UOW-enabled scope (e.g., inside a UnitOfWork attribute on an app service).
  4. If overriding DistributedCache, only call GetUnitOfWorkCache when ShouldConsiderUow returns true.

Example fix

// before: cache call outside a unit of work with considerUow true
await _cache.SetAsync(key, value, considerUow: true); // throws: no active UOW

// after: open a unit of work first
await using (var uow = _unitOfWorkManager.Begin(requiresNew: true))
{
    await _cache.SetAsync(key, value, considerUow: true);
    await uow.CompleteAsync();
}

// or explicitly skip UOW participation
await _cache.SetAsync(key, value, considerUow: false);
Defensive patterns

Strategy: validation

Validate before calling

// Only pass considerUow:true when a UOW is active.
var considerUow = unitOfWorkManager.Current != null;
await cache.SetAsync(key, value, considerUow: considerUow);

Type guard

public static bool HasActiveUnitOfWork(IUnitOfWorkManager uowManager) =>
    uowManager.Current is not null;

Try / catch

try
{
    await cache.SetAsync(key, value, considerUow: true);
}
catch (AbpException ex) when (ex.Message.Contains("no active UOW", StringComparison.Ordinal))
{
    // Either begin a UOW or repeat the call with considerUow: false.
    logger.LogWarning(ex, "Cache write attempted without a UOW; retrying without UOW participation.");
    await cache.SetAsync(key, value, considerUow: false);
}

Prevention

When it happens

Trigger: A code path invokes a cache write/read with considerUow=true (or directly calls GetUnitOfWorkCache) while no unit of work is active. Typically this is an internal path bug or a caller that set considerUow=true without guaranteeing a UOW, e.g., a background service or static initializer that did not open a UOW via IUnitOfWorkManager.Begin.

Common situations: Calling IDistributedCache methods with considerUow:true from a hosted service, IHostedService, or a constructor where no UOW has been begun; calling from an event handler that runs outside the request/UOW scope; a custom subclass overriding cache methods and invoking GetUnitOfWorkCache unconditionally.

Related errors


AI-assisted analysis of abpframework/abp@7ed43b1931 (2026-08-13). Data as JSON: /api/errors/2a121cf99afc0d99. Report an issue: GitHub.