microsoft/aspire · error · InvalidOperationException
Failed to register cancellation token with ID
Error message
Failed to register cancellation token with ID '{id}' What it means
CancellationTokenRegistry.Create generates an id (ct_N) and a fresh CancellationTokenSource, then atomically inserts them into a ConcurrentDictionary. If TryAdd fails — meaning a token with that same id is somehow already registered — it disposes the CTS and throws this InvalidOperationException. Under normal operation the incrementing counter makes collisions impossible, so this is a defensive invariant check against registry corruption or reuse.
Solutions
- Do not manipulate the registry's counter or dictionary from outside; create tokens only via Create/CreateLinked.
- Replace the registry instance (or recreate it) if its internal state has been corrupted.
- Verify no custom code adds 'ct_*' keys directly into the underlying ConcurrentDictionary.
- If seen after patching assemblies, restore the stock CancellationTokenRegistry implementation.
Defensive patterns
Strategy: try-catch
Try / catch
try { var (id, token) = registry.Create(); }
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Failed to register cancellation token"))
{ log.LogError(ex, "Cancellation registry state corrupted; recreating registry"); registry = new CancellationTokenRegistry(); throw; } Prevention
- Create tokens only through the registry's public Create/CreateLinked APIs.
- Never inject or mutate ct_* entries in the underlying dictionary.
- Treat repeated occurrences as a symptom of patched/corrupted registry state.
When it happens
Trigger: Calling CancellationTokenRegistry.Create when _sources already contains the generated ct_N id — only possible if the counter and dictionary got out of sync (e.g. a custom subclass reseeded the counter, or entries were manually injected).
Common situations: Practically never hit in production; seen when tests or tools manipulate registry internals, or when a patched/replaced registry implementation breaks the monotonic-counter assumption.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Request was cancelled
- A validation message must be provided for a failed…
- An interaction with ID
- Azure operation ' ' is already running or queued for the…
- Callback not found
AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16).
Data as JSON: /api/errors/9991c3e1ea8eeaa3.
Report an issue: GitHub.
Appendix: source
Thrown at src/Aspire.Hosting.RemoteHost/CancellationTokenRegistry.cs:35
private int _counter;
private bool _disposed;
/// <summary>
/// Creates a new CancellationTokenSource and returns its ID and token.
/// The ID can be passed to the guest, which can use it to cancel the token.
/// </summary>
/// <returns>A tuple of (tokenId, cancellationToken).</returns>
public (string TokenId, CancellationToken Token) Create()
{
ObjectDisposedException.ThrowIf(_disposed, this);
var id = $"ct_{Interlocked.Increment(ref _counter)}";
var cts = new CancellationTokenSource();
if (!_sources.TryAdd(id, cts))
{
cts.Dispose();
throw new InvalidOperationException($"Failed to register cancellation token with ID '{id}'");
}
return (id, cts.Token);
}
/// <summary>
/// Creates a new CancellationTokenSource linked to an existing token.
/// This is useful when you need to combine multiple cancellation sources.
/// </summary>
/// <param name="linkedToken">The token to link to.</param>
/// <returns>A tuple of (tokenId, cancellationToken).</returns>
public (string TokenId, CancellationToken Token) CreateLinked(CancellationToken linkedToken)
{
ObjectDisposedException.ThrowIf(_disposed, this);
var id = $"ct_{Interlocked.Increment(ref _counter)}";
var cts = CancellationTokenSource.CreateLinkedTokenSource(linkedToken);
View on GitHub (pinned to 25830f84bd)