microsoft/aspire · error · ArgumentException

Cancellation token must be cancellable in order to prevent…

Error message

Cancellation token must be cancellable in order to prevent leaking resources when following logs.

What it means

ResourceLogSource streams logs from DCP resources. In follow mode the stream never completes on its own, so its resources are only cleaned up when the caller's cancellation token fires; a token that cannot be canceled (CancellationToken.None) would leak the stream. GetAsyncEnumerator therefore throws ArgumentException when follow=true and the token is non-cancellable.

Solutions

  1. Pass a cancellable token, e.g. CancellationTokenSource.CreateLinkedTokenSource(applicationStopping).Token.
  2. Create a CancellationTokenSource whose token you pass to the enumerator.
  3. If you only want a snapshot, pass follow: false — a non-cancellable token is then allowed.

Example fix

// before
await foreach (var log in logSource.WatchLogs(follow: true, CancellationToken.None)) { }
// after
using var cts = new CancellationTokenSource();
await foreach (var log in logSource.WatchLogs(follow: true, cts.Token)) { }
Defensive patterns

Strategy: validation

Validate before calling

if (follow && !cancellationToken.CanBeCanceled)
    throw new ArgumentException("follow requires a cancellable token.", nameof(cancellationToken));

Try / catch

try { await foreach (var line in source.WatchLogs(follow: true, token)) { ... } }
catch (ArgumentException ex) when (ex.ParamName == nameof(cancellationToken)) { /* pass a real CTS token */ }

Prevention

When it happens

Trigger: Calling a log-streaming API (Watch/Stream logs) with follow: true and CancellationToken.None, or a default CancellationToken parameter that never gets a real token.

Common situations: Calling WatchLogs from a console host without a linked token, forgetting to pass an application-stopping token, or a library wrapper that forwards CancellationToken.None.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of microsoft/aspire@25830f84bd (2026-09-16). Data as JSON: /api/errors/476b76a16e139c2f. Report an issue: GitHub.

Appendix: source

Thrown at src/Aspire.Hosting/Dcp/ResourceLogSource.cs:40

internal sealed class ResourceLogSource<TResource>(
    ILogger logger,
    IKubernetesService kubernetesService,
    TResource resource,
    bool follow) :
    IAsyncEnumerable<IReadOnlyList<ResourceLogEntry>>
    where TResource : CustomResource, IKubernetesStaticMetadata
{
    public async IAsyncEnumerator<IReadOnlyList<ResourceLogEntry>> GetAsyncEnumerator(CancellationToken cancellationToken)
    {
        // For follow mode, we require a cancellable token to stop streaming.
        // For non-follow mode (snapshot), streams complete naturally so we create our own cancellable token if needed.
        CancellationTokenSource? ownedCts = null;
        if (!cancellationToken.CanBeCanceled)
        {
            if (follow)
            {
                throw new ArgumentException("Cancellation token must be cancellable in order to prevent leaking resources when following logs.", nameof(cancellationToken));
            }
            // Create our own cancellable token for the APIs that require it.
            // For non-follow mode, streams complete naturally when all logs are read.
            ownedCts = new CancellationTokenSource();
            cancellationToken = ownedCts.Token;
        }

        var channel = Channel.CreateUnbounded<ResourceLogEntry>(new UnboundedChannelOptions
        {
            AllowSynchronousContinuations = false,
            SingleReader = true,
            SingleWriter = false
        });

        async Task StreamLogsAsync(Stream stream, bool isError, bool parseDcpLogs)
        {
            try
            {

View on GitHub (pinned to 25830f84bd)