dotnet/orleans · error · ArgumentException

does not support a user supplied .

Error message

{nameof(AdoNetQueueAdapter)} does not support a user supplied {nameof(StreamSequenceToken)}.

What it means

Thrown by AdoNetQueueAdapter.QueueMessageBatchAsync when the caller supplies a non-null StreamSequenceToken. The ADO.NET streaming provider is non-rewindable: it cannot replay from an arbitrary offset, so user-supplied tokens are unsupported and rejected upfront rather than silently ignored.

Solutions

  1. Pass null for the StreamSequenceToken when producing to the ADO.NET adapter.
  2. If you need rewindability, switch to a rewindable streaming provider instead of ADO.NET.
  3. Audit any custom producer/adapter wrapper to ensure it forwards null tokens to ADO.NET.

Example fix

// before
await adapter.QueueMessageBatchAsync(streamId, events, new EventSequenceTokenV2(5), requestContext);

// after
await adapter.QueueMessageBatchAsync(streamId, events, token: null, requestContext);
Defensive patterns

Strategy: type-guard

Type guard

// ADO.NET adapter is non-rewindable; only null tokens are valid.
static bool IsValidTokenForAdoNet(StreamSequenceToken? token) => token is null;

Try / catch

try { await adapter.QueueMessageBatchAsync(streamId, events, token, ctx); }
catch (ArgumentException ex) when (ex.Message.Contains("does not support a user supplied"))
{
    // drop the token and retry with null.
}

Prevention

When it happens

Trigger: QueueMessageBatchAsync is called with a non-null token argument. In normal Orleans usage the streaming runtime passes null for the token on a non-rewindable provider, so this fires only if custom code drives the adapter directly with a token.

Common situations: Custom producer code calling the queue adapter with a checkpoint token; porting rewindable-provider code (e.g. Azure Event Hubs) to ADO.NET without dropping the token; an IStreamQueueMapper/adapter wrapper forwarding tokens.

Related errors


AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13). Data as JSON: /api/errors/8602eb58147b05cb. Report an issue: GitHub.

Appendix: source

Thrown at src/AdoNet/Orleans.Streaming.AdoNet/AdoNetQueueAdapter.cs:39

    /// The ADO.NET provider works both ways.
    /// </summary>
    public StreamProviderDirection Direction => StreamProviderDirection.ReadWrite;

    public IQueueAdapterReceiver CreateReceiver(QueueId queueId)
    {
        // map the queue id
        var adoNetQueueId = mapper.GetAdoNetQueueId(queueId);

        // create the receiver
        return ReceiverFactory(serviceProvider, [Name, adoNetQueueId, streamOptions, clusterOptions, cacheOptions, queries]);
    }

    public async Task QueueMessageBatchAsync<T>(StreamId streamId, IEnumerable<T> events, StreamSequenceToken? token, Dictionary<string, object>? requestContext)
    {
        // the ADO.NET provider is not rewindable so we do not support user supplied tokens
        if (token is not null)
        {
            throw new ArgumentException($"{nameof(AdoNetQueueAdapter)} does not support a user supplied {nameof(StreamSequenceToken)}.");
        }

        // map the Orleans stream id to the corresponding queue id
        var queueId = mapper.GetAdoNetQueueId(streamId);

        // create the payload from the events
        var payload = AdoNetBatchContainer.ToMessagePayload(serializer, streamId, events.Cast<object>().ToList(), requestContext);

        // we can enqueue the message now
        try
        {
            await queries.QueueStreamMessageAsync(clusterOptions.ServiceId, Name, queueId, payload, streamOptions.ExpiryTimeout.TotalSecondsCeiling());
        }
        catch (Exception ex)
        {
            LogFailedToQueueStreamMessage(ex, clusterOptions.ServiceId, Name, queueId);
            throw;
        }

View on GitHub (pinned to fca799fa70)