{"record":{"id":"a24a44c53ffe9bc4","repo":"dotnet/orleans","slug":"streamid","errorCode":null,"errorMessage":"streamId","messagePattern":"streamId","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Orleans.BroadcastChannel/IdMapping/DefaultChannelIdMapper.cs","lineNumber":53,"sourceCode":"                        && string.Equals(value, \"true\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        includeNamespaceInGrainId = true;\n                    }\n                }\n            }\n\n            return keyType switch\n            {\n                nameof(Guid) => GetGuidKey(streamId, includeNamespaceInGrainId),\n                nameof(Int64) => GetIntegerKey(streamId, includeNamespaceInGrainId),\n                _ => streamId.GetKeyIdSpan(), // null or string\n            };\n        }\n\n        private static IdSpan GetGuidKey(ChannelId streamId, bool includeNamespaceInGrainId)\n        {\n            var key = streamId.Key.Span;\n            if (!Utf8Parser.TryParse(key, out Guid guidKey, out var len, 'N') || len < key.Length) throw new ArgumentException(nameof(streamId));\n\n            if (!includeNamespaceInGrainId)\n                return streamId.GetKeyIdSpan();\n\n            var ns = streamId.Namespace.Span;\n            return ns.IsEmpty ? streamId.GetKeyIdSpan() : GrainIdKeyExtensions.CreateGuidKey(guidKey, ns);\n        }\n\n        private static IdSpan GetIntegerKey(ChannelId streamId, bool includeNamespaceInGrainId)\n        {\n            var key = streamId.Key.Span;\n            if (!Utf8Parser.TryParse(key, out int intKey, out var len) || len < key.Length) throw new ArgumentException(nameof(streamId));\n\n            return includeNamespaceInGrainId\n                ? GrainIdKeyExtensions.CreateIntegerKey(intKey, streamId.Namespace.Span)\n                : GrainIdKeyExtensions.CreateIntegerKey(intKey);\n        }\n    }","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/dotnet/orleans/blob/fca799fa70ecb6ad975224271703ca43221f58de/src/Orleans.BroadcastChannel/IdMapping/DefaultChannelIdMapper.cs#L35-L71","documentation":"Thrown by DefaultChannelIdMapper.GetGuidKey when a legacy Guid-keyed subscriber grain (IGrainWithGuidKey or IGrainWithGuidCompoundKey) is implicitly subscribed to a broadcast channel, but the ChannelId's Key bytes cannot be parsed as a Guid in 'N' format (32 hex characters, no dashes). The mapper must convert the channel key into the grain's Guid primary key; if the key is not a valid Guid, mapping fails. This fires inside BroadcastChannelWriter.Publish because the writer must construct a grain reference for every matching implicit subscriber.","triggerScenarios":"A publisher calls BroadcastChannelWriter.Publish on a ChannelId whose Key is not parseable as a Guid, while the matched subscriber grain implements IGrainWithGuidKey. The grain's legacy-grain-key-type binding is 'Guid', which routes GetGrainKeyId into the GetGuidKey branch where Utf8Parser.TryParse fails. Concretely: ChannelId.Create(\"ns\", \"not-a-guid\") or ChannelId.Create(\"ns\", someInteger.ToString()) with a Guid-keyed subscriber, or ChannelId.Create(\"ns\", someGuid) where the Guid is formatted with dashes ('D' format) instead of 'N'.","commonSituations":"The publisher and subscriber teams disagree on the channel key type — the publisher uses a string or integer key while the subscriber grain's interface extends IGrainWithGuidKey. A grain is refactored from IGrainWithStringKey to IGrainWithGuidKey without updating the publishing code. A Guid is formatted with dashes (Guid.ToString() default 'D' format) instead of 'N' before being used as a raw key byte sequence via ChannelId.Create(ns, guidString).","solutions":["Use the ChannelId.Create(string ns, Guid key) overload, which formats the Guid in 'N' format internally and guarantees compatibility with Guid-keyed subscriber grains.","Change the subscriber grain to implement IGrainWithStringKey instead of IGrainWithGuidKey so the default mapper arm uses the key bytes directly without Guid parsing.","Implement a custom IChannelIdMapper that handles the key conversion, register it as a keyed singleton in DI, and reference it via ImplicitChannelSubscription(streamNamespace, channelIdMapper: \"your-mapper-name\")."],"exampleFix":"// before — string key with a Guid-keyed subscriber grain\nvar channelId = ChannelId.Create(\"orders\", \"order-123\");\nvar writer = provider.GetChannelWriter<OrderEvent>(channelId);\nawait writer.Publish(evt); // throws ArgumentException(nameof(streamId))\n\n// after — Guid key matching the grain's IGrainWithGuidKey interface\nvar channelId = ChannelId.Create(\"orders\", orderGuid);\nvar writer = provider.GetChannelWriter<OrderEvent>(channelId);\nawait writer.Publish(evt);","handlingStrategy":"validation","validationCode":"// Validate the channel key is a valid Guid 'N' format before publishing,\n// when the subscriber grain implements IGrainWithGuidKey.\nstatic void ValidateGuidChannelKey(ChannelId channelId)\n{\n    var keyStr = System.Text.Encoding.UTF8.GetString(channelId.Key.Span);\n    if (!Guid.TryParseExact(keyStr, \"N\", out _))\n        throw new InvalidOperationException(\n            $\"ChannelId key '{keyStr}' is not a valid Guid ('N' format). \" +\n            \"The subscriber grain is Guid-keyed; use ChannelId.Create(namespace, guid) instead.\");\n}","typeGuard":"// Returns true if the ChannelId key is a valid Guid 'N' format.\nstatic bool HasGuidKey(ChannelId channelId)\n{\n    var keyStr = System.Text.Encoding.UTF8.GetString(channelId.Key.Span);\n    return Guid.TryParseExact(keyStr, \"N\", out _);\n}","tryCatchPattern":"try\n{\n    await writer.Publish(item);\n}\ncatch (ArgumentException ex) when (ex.ParamName == \"streamId\")\n{\n    logger.LogWarning(ex, \"ChannelId key does not match subscriber grain key type. \" +\n        \"Ensure the channel key is a valid Guid for Guid-keyed grains.\");\n}","preventionTips":["Use the ChannelId.Create(string ns, Guid key) overload for Guid-keyed subscriber grains — it formats the Guid in 'N' format internally.","Align the channel key type with the subscriber grain's marker interface (IGrainWithGuidKey, IGrainWithIntegerKey, or IGrainWithStringKey).","Document the expected key type on each subscriber grain so publishers know which ChannelId.Create overload to use."],"tags":["broadcast-channel","key-mapping","argument","configuration"],"backgroundTag":null,"analyzedSha":"fca799fa70ecb6ad975224271703ca43221f58de","analyzedAt":"2026-08-13T19:55:57.938Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}