{"record":{"id":"3f04aa622d57ca2e","repo":"dotnet/orleans","slug":"specified-method-is-not-supported-3f04aa","errorCode":null,"errorMessage":"Specified method is not supported.","messagePattern":"Specified method is not supported\\.","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"src/Azure/Orleans.Streaming.EventHubs/Providers/Streams/EventHub/EventHubAdapterReceiver.cs","lineNumber":405,"sourceCode":"        {\n            (this.receiver as EventHubPartitionGeneratorReceiver)?.StopProducingOnStream(streamId);\n        }\n\n        [GenerateSerializer]\n        internal class StreamActivityNotificationBatch : IBatchContainer\n        {\n            [Id(0)]\n            public StreamPosition Position { get; }\n\n            public StreamId StreamId => this.Position.StreamId;\n            public StreamSequenceToken SequenceToken => this.Position.SequenceToken;\n\n            public StreamActivityNotificationBatch(StreamPosition position)\n            {\n                this.Position = position;\n            }\n\n            public IEnumerable<Tuple<T, StreamSequenceToken>> GetEvents<T>() { throw new NotSupportedException(); }\n            public bool ImportRequestContext() { throw new NotSupportedException(); }\n        }\n\n        private class Cursor : IQueueCacheCursor\n        {\n            private readonly IEventHubQueueCache cache;\n            private readonly object cursor;\n            private IBatchContainer? current;\n\n            public Cursor(IEventHubQueueCache cache, StreamId streamId, StreamSequenceToken? token)\n            {\n                this.cache = cache;\n                this.cursor = cache.GetCursor(streamId, token);\n            }\n\n            public void Dispose()\n            {\n            }","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/dotnet/orleans/blob/fca799fa70ecb6ad975224271703ca43221f58de/src/Azure/Orleans.Streaming.EventHubs/Providers/Streams/EventHub/EventHubAdapterReceiver.cs#L387-L423","documentation":"StreamActivityNotificationBatch is an internal IBatchContainer implementation used by EventHubAdapterReceiver solely to signal stream activity (a new message arrived) — it carries only a StreamPosition and does not hold actual event payloads. Its GetEvents<T>() and ImportRequestContext() methods throw NotSupportedException because the batch has no deserializable events and no request context to import. Calling these methods indicates the consumer is treating a notification-only batch as a full data batch.","triggerScenarios":"Code that receives an IBatchContainer from the EventHub stream's EventHubAdapterReceiver.GetQueueMessagesAsync and calls GetEvents<T>() or ImportRequestContext() on it. This happens when a consumer bypasses the IQueueCacheCursor path (which resolves the real batch container from the cache) and instead directly processes the StreamActivityNotificationBatch objects returned by GetQueueMessagesAsync.","commonSituations":"A custom stream extension or observer infrastructure that enumerates IBatchContainer.GetEvents<T>() on batches obtained from the receiver before they go through cache cursor resolution; incorrect assumption that all IBatchContainer instances support GetEvents; an Orleans internal code path regression after an upgrade.","solutions":["Do not call GetEvents<T>() or ImportRequestContext() directly on batches from GetQueueMessagesAsync — these are activity notifications, not data containers.","Use the IQueueCacheCursor (obtained via GetCacheCursor) to read actual event data, which resolves the real EventHubBatchContainer from the cache.","If building custom streaming infrastructure, check the batch type before calling GetEvents — if it is StreamActivityNotificationBatch, treat it as a signal only."],"exampleFix":"// before\nvar batches = await receiver.GetQueueMessagesAsync(maxCount, ct);\nforeach (var batch in batches)\n{\n    var events = batch.GetEvents<MyEvent>(); // throws NotSupportedException\n}\n\n// after\nvar batches = await receiver.GetQueueMessagesAsync(maxCount, ct);\n// batches are activity notifications; actual events are read via cache cursor\nvar cursor = receiver.GetCacheCursor(streamId, token);\nwhile (cursor.MoveNext())\n{\n    var batch = cursor.GetCurrent(out var ex);\n    // batch is now the real EventHubBatchContainer with actual events\n}","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"// Check if the batch is a notification-only container before calling GetEvents:\nstatic bool IsDataBatch(IBatchContainer batch)\n    => batch.GetType().Name is not \"StreamActivityNotificationBatch\";\n\n// Or check capability before calling:\nstatic IEnumerable<Tuple<T, StreamSequenceToken>>? SafeGetEvents<T>(IBatchContainer batch)\n{\n    if (batch.GetType().Name == \"StreamActivityNotificationBatch\")\n        return null; // notification-only, no events\n    try { return batch.GetEvents<T>(); }\n    catch (NotSupportedException) { return null; }\n}","tryCatchPattern":null,"preventionTips":["Never call GetEvents<T>() or ImportRequestContext() on IBatchContainer objects from GetQueueMessagesAsync — use the cache cursor instead.","Use IQueueCacheCursor (GetCacheCursor/MoveNext/GetCurrent) to read actual event data from the cache.","If building custom stream infrastructure, type-check batches before invoking data-access methods."],"tags":["not-supported","batch-container","eventhub","internal","stream-activity"],"backgroundTag":null,"analyzedSha":"fca799fa70ecb6ad975224271703ca43221f58de","analyzedAt":"2026-08-13T19:55:57.938Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}