dotnet/orleans · error · ArgumentException

The channel ID doesn't have an associated namespace.

Error message

The channel ID doesn't have an associated namespace.

What it means

Thrown by ImplicitChannelSubscriberTable.GetImplicitSubscribers when a publish operation targets a ChannelId that has no namespace. Implicit broadcast channel subscriptions are matched exclusively by namespace predicate (AllStreamNamespacesPredicate, ExactMatchChannelNamespacePredicate, RegexChannelNamespacePredicate), so a channel without a namespace cannot match any subscriber and is rejected at the boundary. This fires inside BroadcastChannelWriter.Publish on its first line (line 54), before any subscriber lookup occurs.

Source

Thrown at src/Orleans.BroadcastChannel/SubscriberTable/ImplicitChannelSubscriberTable.cs:122

            }

            return new Cache(version, newPredicates);
        }

        /// <summary>
        /// Retrieve a map of implicit subscriptionsIds to implicit subscribers, given a channel ID. This method throws an exception if there's no namespace associated with the channel ID.
        /// </summary>
        /// <param name="channelId">A channel ID.</param>
        /// <param name="grainFactory">The grain factory used to get consumer references.</param>
        /// <returns>A set of references to implicitly subscribed grains. They are expected to support the broadcast channel consumer extension.</returns>
        /// <exception cref="ArgumentException">The channel ID doesn't have an associated namespace.</exception>
        /// <exception cref="InvalidOperationException">Internal invariant violation.</exception>
        internal Dictionary<Guid, IBroadcastChannelConsumerExtension> GetImplicitSubscribers(InternalChannelId channelId, IGrainFactory grainFactory)
        {
            var channelNamespace = channelId.GetNamespace();
            if (string.IsNullOrWhiteSpace(channelNamespace))
            {
                throw new ArgumentException("The channel ID doesn't have an associated namespace.", nameof(channelId));
            }

            var entries = GetOrAddImplicitSubscribers(channelNamespace);

            var result = new Dictionary<Guid, IBroadcastChannelConsumerExtension>();
            foreach (var entry in entries)
            {
                var consumer = MakeConsumerReference(grainFactory, channelId, entry);
                var subscriptionGuid = MakeSubscriptionGuid(entry.GrainType, channelId);
                CollectionsMarshal.GetValueRefOrAddDefault(result, subscriptionGuid, out var duplicate) = consumer;
                if (duplicate)
                {
                    throw new InvalidOperationException(
                        $"Internal invariant violation: generated duplicate subscriber reference: {consumer}, subscriptionId: {subscriptionGuid}");
                }
            }
            return result;
        }

View on GitHub (pinned to fca799fa70)

Solutions

  1. Always provide a non-empty, non-whitespace namespace string when creating a ChannelId for broadcast publishing: ChannelId.Create("my-namespace", key).
  2. If you need to publish to subscribers across multiple namespaces, create a separate ChannelId per namespace.
  3. Ensure the namespace you choose matches at least one subscriber grain's channel-pattern predicate (e.g., 'namespace:my-namespace' or 'regex:my-.*').

Example fix

// before — no namespace
var channelId = ChannelId.Create(null, grainKey);
var writer = provider.GetChannelWriter<Event>(channelId);
await writer.Publish(evt); // throws ArgumentException(nameof(channelId))

// after — namespace provided
var channelId = ChannelId.Create("events", grainKey);
var writer = provider.GetChannelWriter<Event>(channelId);
await writer.Publish(evt);
Defensive patterns

Strategy: validation

Validate before calling

// Validate the ChannelId has a non-empty namespace before publishing.
static void ValidateChannelHasNamespace(ChannelId channelId)
{
    if (string.IsNullOrWhiteSpace(channelId.GetNamespace()))
        throw new InvalidOperationException(
            "Broadcast channel publishing requires a non-empty namespace. " +
            "Use ChannelId.Create(namespace, key) with a non-null namespace.");
}

Type guard

// Returns true if the ChannelId has a non-empty namespace.
static bool HasNamespace(ChannelId channelId)
    => !string.IsNullOrWhiteSpace(channelId.GetNamespace());

Try / catch

try
{
    await writer.Publish(item);
}
catch (ArgumentException ex) when (ex.ParamName == "channelId"
    && ex.Message.Contains("namespace"))
{
    logger.LogWarning(ex, "ChannelId has no namespace. " +
        "Provide a non-empty namespace when creating the ChannelId.");
}

Prevention

When it happens

Trigger: A publisher calls BroadcastChannelWriter.Publish on a ChannelId created without a namespace, meaning keyIndex is 0 and GetNamespace() returns null. This happens with ChannelId.Create(null, key), ChannelId.Create(null, guidKey), or ChannelId.Create((ReadOnlySpan<byte>)null, key). The null/empty namespace triggers the string.IsNullOrWhiteSpace guard in GetImplicitSubscribers before any predicate matching runs.

Common situations: A developer passes null for the namespace parameter of ChannelId.Create, assuming it is optional. A ChannelId constructed for a different purpose (e.g., a direct grain reference) is reused for broadcast publishing without adding a namespace. Test code creates a ChannelId with only a key and forgets the namespace.

Related errors


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