{"record":{"id":"c7ee6da523374aa2","repo":"dotnet/orleans","slug":"inform-grain-id-in-format-id-additionalkey","errorCode":null,"errorMessage":"Inform grain id in format `{ id},{additionalKey}`","messagePattern":"Inform grain id in format `(.+?),(.+?)`","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Dashboard/Orleans.Dashboard/Implementation/Helpers/GrainStateHelper.cs","lineNumber":22,"sourceCode":"using System.Linq;\nusing System.Reflection;\n\nnamespace Orleans.Dashboard.Implementation.Helpers;\n\ninternal static class GrainStateHelper\n{\n    public static (object?, string) GetGrainId(string id, Type implementationType)\n    {\n        object? grainId = null;\n        string keyExtension = \"\";\n        var splitedGrainId = id.Split(\",\");\n\n        try\n        {\n            if (implementationType.IsAssignableTo(typeof(IGrainWithGuidCompoundKey)))\n            {\n                if (splitedGrainId.Length != 2)\n                    throw new InvalidOperationException(\"Inform grain id in format `{ id},{additionalKey}`\");\n\n                grainId = Guid.Parse(splitedGrainId.First());\n                keyExtension = splitedGrainId.Last();\n            }\n            else if (implementationType.IsAssignableTo(typeof(IGrainWithIntegerCompoundKey)))\n            {\n                if (splitedGrainId.Length != 2)\n                    throw new InvalidOperationException(\"Inform grain id in format {id},{additionalKey}\");\n\n                grainId = Convert.ToInt64(splitedGrainId.First());\n                keyExtension = splitedGrainId.Last();\n            }\n            else if (implementationType.IsAssignableTo(typeof(IGrainWithIntegerKey)))\n            {\n                grainId = Convert.ToInt64(id);\n            }\n            else if (implementationType.IsAssignableTo(typeof(IGrainWithGuidKey)))\n            {","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/dotnet/orleans/blob/fca799fa70ecb6ad975224271703ca43221f58de/src/Dashboard/Orleans.Dashboard/Implementation/Helpers/GrainStateHelper.cs#L4-L40","documentation":"InvalidOperationException from GrainStateHelper.GetGrainId when the grain's implementation type implements IGrainWithGuidCompoundKey but the supplied id string does not split into exactly two comma-separated parts. The compound-guid format requires exactly 'guid,additionalKey' (the message has a stray backtick/braces typo but means the same format).","triggerScenarios":"Dashboard invokes GetGrainId(id, implementationType) where implementationType is IGrainWithGuidCompoundKey and id does not contain exactly one comma (splitedGrainId.Length != 2). E.g. id='abc' or id='g1,g2,g3'.","commonSituations":"Dashboard grain-state inspection receives a malformed id from the frontend, a grain registered with a compound key invoked through a path that passes the raw key without the extension, or a client serializing only the primary key. The downstream Guid.Parse can also throw (wrapped by error 357).","solutions":["Ensure ids for compound-key grains are passed as 'guid,additionalKey' with exactly one comma.","Validate the format on the client/frontend before requesting grain state.","If using single-key grains, make sure the grain interface marker matches (IGrainWithGuidKey vs IGrainWithGuidCompoundKey)."],"exampleFix":"// before\nvar (gid, ext) = GrainStateHelper.GetGrainId(rawId, typeof(MyCompoundGrain));\n// rawId = \"abc\" -> throws\n\n// after\nvar parts = rawId.Split(',');\nif (parts.Length != 2 || !Guid.TryParse(parts[0], out _))\n    return BadRequest(\"id must be 'guid,additionalKey'\");\nvar (gid, ext) = GrainStateHelper.GetGrainId(rawId, typeof(MyCompoundGrain));","handlingStrategy":"validation","validationCode":"var parts = id.Split(',');\nif (parts.Length != 2 || !Guid.TryParse(parts[0], out _))\n    return BadRequest(\"id must be 'guid,additionalKey'\");\nvar (gid, ext) = GrainStateHelper.GetGrainId(id, type);","typeGuard":"static bool IsValidGuidCompoundId(string id) =>\n    id.Split(',') is { Length: 2 } p && Guid.TryParse(p[0], out _);","tryCatchPattern":"try { var (gid, ext) = GrainStateHelper.GetGrainId(id, type); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"grain id in format\"))\n{ return BadRequest(\"compound guid id malformed\"); }","preventionTips":["Validate the 'guid,key' format on the client/frontend","Match grain interface markers to the key shape","Never pass a single-segment id to a compound-key grain"],"tags":["dashboard","grain-id","argument-validation","parsing"],"backgroundTag":null,"analyzedSha":"fca799fa70ecb6ad975224271703ca43221f58de","analyzedAt":"2026-08-13T19:55:57.938Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}