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
- Always provide a non-empty, non-whitespace namespace string when creating a ChannelId for broadcast publishing: ChannelId.Create("my-namespace", key).
- If you need to publish to subscribers across multiple namespaces, create a separate ChannelId per namespace.
- 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
- Always pass a non-empty namespace string to ChannelId.Create when publishing to a broadcast channel.
- Centralize ChannelId construction in a factory method that enforces a non-null namespace argument.
- Validate channelId.GetNamespace() before calling Publish in test and production code.
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
- Journal metadata property names must not contain null charac
- MaxMetadataOnlyConflictRetries must be non-negative.
- MetadataOnlyConflictInitialBackoff must be non-negative.
- MetadataOnlyConflictMaxBackoff must be non-negative.
- The journal id must not be the default value.
AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13).
Data as JSON: /api/errors/24615da81e9ad45f.
Report an issue: GitHub.