dotnet/orleans · warning · NotSupportedException
This adapter only supports read
Error message
This adapter only supports read
What it means
Thrown by the CustomDataAdapter sample's ToQueueMessage override to make the stream adapter explicitly write-only-incompatible — the sample demonstrates a read-only Event Hubs consumer adapter and intentionally forbids producing. Any code path that tries to publish events through this provider hits the NotSupportedException by design.
Source
Thrown at samples/Streaming/CustomDataAdapter/Silo/CustomDataAdapter.cs:28
// Custom EventHubDataAdapter that serialize event using System.Text.Json
public class CustomDataAdapter : EventHubDataAdapter
{
public CustomDataAdapter(Serializer serializer) : base(serializer)
{
}
public override string GetPartitionKey(StreamId streamId)
=> streamId.ToString();
public override StreamId GetStreamIdentity(EventData queueMessage)
{
var guid = Guid.Parse(queueMessage.PartitionKey);
var ns = (string) queueMessage.Properties["StreamNamespace"];
return StreamId.Create(ns, guid);
}
public override EventData ToQueueMessage<T>(StreamId streamId, IEnumerable<T> events, StreamSequenceToken? token, Dictionary<string, object>? requestContext)
=> throw new NotSupportedException("This adapter only supports read");
protected override IBatchContainer GetBatchContainer(EventHubMessage eventHubMessage)
=> new CustomBatchContainer(eventHubMessage);
}
[GenerateSerializer, Immutable]
public sealed class CustomBatchContainer : IBatchContainer
{
[Id(0)]
private readonly EventHubMessage _eventHubMessage;
[Id(1)]
public StreamSequenceToken SequenceToken { get; }
public StreamId StreamId => _eventHubMessage.StreamId;
public CustomBatchContainer(EventHubMessage eventHubMessage)
{View on GitHub (pinned to fca799fa70)
Solutions
- Do not produce through this provider — it is a consumer-only sample. Publish through a different stream provider configured with a full bidirectional adapter.
- If you need bidirectional support, implement ToQueueMessage in your own adapter instead of inheriting the sample's throw.
- Verify which provider name a producer grain targets (Constants.StreamProvider) versus the one configured with CustomDataAdapter.
Example fix
// before: producing through the read-only sample adapter var stream = provider.GetStream<int>(streamId); await stream.OnNextAsync(1); // hits ToQueueMessage -> NotSupportedException // after: publish via a provider whose adapter implements ToQueueMessage var writeStream = writeProvider.GetStream<int>(streamId); await writeStream.OnNextAsync(1);
Defensive patterns
Strategy: validation
Validate before calling
// Only subscribe/consume on the read-only provider; publish via a separate provider
if (providerName == "CustomDataAdapter")
throw new InvalidOperationException("Provider is read-only; do not produce."); Type guard
null
Try / catch
try
{
await stream.OnNextAsync(value);
}
catch (NotSupportedException ex) when (ex.Message.Contains("only supports read"))
{
// Route production to a bidirectional provider instead
} Prevention
- Never publish through a consumer-only adapter; publish via a separate provider.
- Implement ToQueueMessage in your own adapter if bidirectional support is needed.
- Document provider directionality in the sample's README.
When it happens
Trigger: A grain or client calls GetStreamProvider(...).GetStream<T>(streamId).OnNextAsync(...) while the provider is configured with CustomDataAdapter, whose ToQueueMessage (the produce path) always throws.
Common situations: Reusing the read-only sample adapter in a project that also needs to publish; wiring the same adapter to a producer grain by mistake; assuming every EventDataBatchContainerAdapter supports both directions.
Related errors
- Cannot find file {filename}
- This grain is already producing events
- dataAdapter
- Recovered {name} does not match. Written: {Serialize(written
- Missing value for {name}.
AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13).
Data as JSON: /api/errors/54fe89eee19500b8.
Report an issue: GitHub.