{"record":{"id":"148da3f4277bf631","repo":"dotnet/orleans","slug":"journal-metadata-property-key-is-provider-owne-148da3","errorCode":null,"errorMessage":"Journal metadata property '{key}' is provider-owned.","messagePattern":"Journal metadata property '(.+?)' is provider-owned\\.","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Azure/Orleans.Journaling.AzureStorage/AzureTableJournalStorage.cs","lineNumber":1177,"sourceCode":"    }\n\n    private static void ValidateCallerMetadataProperty(string key, string value)\n    {\n        ValidateCallerMetadataPropertyName(key);\n        ArgumentNullException.ThrowIfNull(value);\n    }\n\n    private static void ValidateCallerMetadataPropertyName(string key)\n    {\n        ArgumentException.ThrowIfNullOrWhiteSpace(key);\n        if (key.IndexOf('\\0') >= 0)\n        {\n            throw new ArgumentException(\"Journal metadata property names must not contain null characters.\", nameof(key));\n        }\n\n        if (key.StartsWith(\"$\", StringComparison.Ordinal))\n        {\n            throw new ArgumentException($\"Journal metadata property '{key}' is provider-owned.\", nameof(key));\n        }\n    }\n\n    private static ETag ToAzureETag(string eTag)\n    {\n        ArgumentException.ThrowIfNullOrWhiteSpace(eTag);\n        return new ETag(eTag);\n    }\n\n    private static bool IsEntityAlreadyExists(RequestFailedException exception)\n        => exception.Status == 409\n            && (string.Equals(exception.ErrorCode, \"EntityAlreadyExists\", StringComparison.Ordinal)\n                || exception.Message.Contains(\"already exists\", StringComparison.OrdinalIgnoreCase));\n\n    /// <summary>\n    /// Returns true when an Azure response indicates the journal header has been mutated since our\n    /// cached ETag was captured: HTTP 404 (header deleted), HTTP 412 (precondition failed / IfMatch\n    /// rejected), or HTTP 409 with <c>EntityAlreadyExists</c> (a competing writer already published","sourceCodeStart":1159,"sourceCodeEnd":1195,"githubUrl":"https://github.com/dotnet/orleans/blob/fca799fa70ecb6ad975224271703ca43221f58de/src/Azure/Orleans.Journaling.AzureStorage/AzureTableJournalStorage.cs#L1159-L1195","documentation":"A caller-supplied metadata property name starts with '$', which is reserved for provider-owned columns (e.g., the $header RowKey and provider properties). ValidateCallerMetadataPropertyName (AzureTableJournalStorage.cs:1175) rejects these to prevent callers from colliding with or overwriting internal schema fields. The argument blamed is `key`.","triggerScenarios":"Passing a metadata key beginning with '$' to a set/update/remove API; thrown at AzureTableJournalStorage.cs:1177.","commonSituations":"Caller used '$' as a namespace separator ('$namespace$field'); keys auto-prefixed with '$' by a framework; copied internal property names into caller metadata.","solutions":["Use a different separator (':', '.', '/') in caller metadata keys.","Strip a leading '$' before the call if it is decorative.","Reserve the '$' prefix strictly for provider-owned fields."],"exampleFix":"// before\nstorage.SetMetadata(new() { [\"$kind\"] = \"foo\" });\n\n// after\nstorage.SetMetadata(new() { [\"kind\"] = \"foo\" });","handlingStrategy":"validation","validationCode":"foreach (var k in metadata.Keys)\n    if (k.StartsWith('$')) throw new ArgumentException($\"Key '{k}' is provider-owned\");","typeGuard":"static bool IsCallerOwnedKey(string key) => !key.StartsWith('$', StringComparison.Ordinal);","tryCatchPattern":null,"preventionTips":["Reserve the '$' prefix for provider-owned fields.","Use ':', '.', or '/' as namespace separators in caller metadata.","Document the reserved prefix for callers."],"tags":["metadata","validation","api-usage"],"backgroundTag":null,"analyzedSha":"fca799fa70ecb6ad975224271703ca43221f58de","analyzedAt":"2026-08-13T19:55:57.938Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}