elsa-workflows/elsa-core · error · InvalidOperationException
Factory returned null for cache key
Error message
Factory returned null for cache key: {key}. What it means
CacheManager.GetOrCreateAsync wraps IMemoryCache.GetOrCreateAsync and guarantees a non-null result: if the factory returns null, it throws InvalidOperationException naming the cache key. The contract is that a cached value must always be materializable; a null factory result is treated as a programming error.
Solutions
- Make the factory return a non-null value (empty collection, default record, or sentinel) on miss.
- Return the error/throw a domain-specific exception inside the factory instead of returning null.
- If null is legitimate, use IMemoryCache directly rather than CacheManager.GetOrCreateAsync.
Example fix
// before
var user = await cache.GetOrCreateAsync<User>(key, async e => await repo.FindAsync(id)); // may return null
// after
var user = await cache.GetOrCreateAsync<User?>(key, async e => await repo.FindAsync(id))
?? await repo.FindAsync(id); // or have the factory throw/return a sentinel Defensive patterns
Strategy: try-catch
Validate before calling
var existing = memoryCache.Get(key);
// ensure your factory cannot return null:
TItem Load() => repo.Find(id) ?? throw new KeyNotFoundException($"{key} not found"); Try / catch
try { item = await cache.GetOrCreateAsync<TItem>(key, factory); }
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Factory returned null")) { item = fallbackValue; } Prevention
- Ensure factories return non-null sentinels/empty collections on miss.
- Throw domain exceptions from the factory instead of returning null.
- Add unit tests covering cache-miss paths.
When it happens
Trigger: Calling GetOrCreateAsync<TItem> with a factory whose async body returns null (e.g. an entity lookup that finds nothing, or a TryGetValue-style factory).
Common situations: Caching a record lookup whose miss path returns null instead of a sentinel/empty value; refactoring a sync factory to async where the miss branch changed shape.
Related errors
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/25b65f8da4fab74d.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Caching/Services/CacheManager.cs:42
}
/// <inheritdoc />
public async Task<TItem?> FindOrCreateAsync<TItem>(object key, Func<ICacheEntry, Task<TItem>> factory)
{
return await memoryCache.GetOrCreateAsync(key, async entry => await factory(entry));
}
/// <summary>
/// Retrieves a cached item by the specified key or creates a new one using the provided factory function.
/// </summary>
/// <param name="key">The key used to identify the cached item.</param>
/// <param name="factory">A factory function that provides the value to be cached if it does not already exist.</param>
/// <typeparam name="TItem">The type of the item to retrieve or create.</typeparam>
/// <returns>The cached or newly created item.</returns>
/// <exception cref="InvalidOperationException">Thrown if the factory function returns null.</exception>
public async Task<TItem> GetOrCreateAsync<TItem>(object key, Func<ICacheEntry, Task<TItem>> factory)
{
return await memoryCache.GetOrCreateAsync(key, async entry => await factory(entry)) ?? throw new InvalidOperationException($"Factory returned null for cache key: {key}.");
}
}View on GitHub (pinned to fe9217bdfa)