{"record":{"id":"0d529fd779d7b097","repo":"dotnet/orleans","slug":"journal-metadata-property-key-is-provider-owne","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/AzureBlobJournalStorage.cs","lineNumber":1024,"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 (IsProviderMetadataKey(key))\n        {\n            throw new ArgumentException($\"Journal metadata property '{key}' is provider-owned.\", nameof(key));\n        }\n    }\n\n    private static bool IsProviderMetadataKey(string key)\n        => string.Equals(key, FormatMetadataKey, StringComparison.OrdinalIgnoreCase)\n            || string.Equals(key, CheckpointMetadataKey, StringComparison.OrdinalIgnoreCase)\n            || string.Equals(key, CheckpointOffsetMetadataKey, StringComparison.OrdinalIgnoreCase)\n            || string.Equals(key, WalGenerationMetadataKey, StringComparison.OrdinalIgnoreCase)\n            || key.StartsWith(\"$\", StringComparison.Ordinal);\n\n    private static ETag ToAzureETag(string eTag)\n    {\n        ArgumentException.ThrowIfNullOrWhiteSpace(eTag);\n        return new ETag(eTag);\n    }\n\n    /// <summary>\n    /// Returns true when an Azure response indicates an append blob was sealed (HTTP 409 / BlobIsSealed).","sourceCodeStart":1006,"sourceCodeEnd":1042,"githubUrl":"https://github.com/dotnet/orleans/blob/fca799fa70ecb6ad975224271703ca43221f58de/src/Azure/Orleans.Journaling.AzureStorage/AzureBlobJournalStorage.cs#L1006-L1042","documentation":"Thrown by AzureBlobJournalStorage when a caller-supplied metadata property key collides with a provider-reserved key. The provider owns 'format', 'checkpoint', 'checkpoint_offset', 'wal_generation' (case-insensitive), and any key starting with '$', because those carry the WAL manifest and recovery bookkeeping. Allowing a caller to overwrite them would corrupt journal recovery.","triggerScenarios":"Passing a metadata dictionary to AppendAsync/ReplaceAsync/CreateIfNotExistsAsync whose key is exactly one of the reserved names (case-insensitive) or starts with '$'. The check runs in ValidateCallerMetadataPropertyName before the write.","commonSituations":"Using generic key names like 'format' or 'checkpoint' for application metadata without realizing they are reserved; migrating from a different storage backend that allowed those names; using a '$'-prefixed convention copied from Azure Cosmos DB.","solutions":["Prefix your application metadata keys with a namespace, e.g. 'app.format' or 'user.checkpoint'.","Avoid keys starting with '$' entirely; that prefix is reserved for future provider bookkeeping.","Check IsProviderMetadataKey semantics: the four named keys plus any '$'-prefixed key are off-limits."],"exampleFix":"// before\nvar meta = new Dictionary<string, string> { [\"format\"] = \"json\", [\"checkpoint\"] = \"v1\" };\nawait storage.AppendAsync(data, meta, ct);\n\n// after\nvar meta = new Dictionary<string, string> { [\"app.format\"] = \"json\", [\"app.checkpoint\"] = \"v1\" };\nawait storage.AppendAsync(data, meta, ct);","handlingStrategy":"validation","validationCode":"static readonly HashSet<string> ReservedKeys = new(StringComparer.OrdinalIgnoreCase)\n    { \"format\", \"checkpoint\", \"checkpoint_offset\", \"wal_generation\" };\n\nstatic bool IsSafeMetadataKey(string key)\n    => !string.IsNullOrWhiteSpace(key)\n        && !key.StartsWith(\"$\", StringComparison.Ordinal)\n        && !ReservedKeys.Contains(key);\n\n// usage\nif (metadata.Keys.All(IsSafeMetadataKey)) { /* safe */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Namespace your application metadata keys to avoid reserved names.","Document the reserved-key list near your metadata-building helpers.","Add a configuration-time test that validates a representative metadata dictionary."],"tags":["azure-blob-storage","metadata","validation","reserved-keys"],"backgroundTag":null,"analyzedSha":"fca799fa70ecb6ad975224271703ca43221f58de","analyzedAt":"2026-08-13T19:55:57.938Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}