dotnet/orleans · error · ArgumentException
Journal metadata property '{key}' is provider-owned.
Error message
Journal metadata property '{key}' is provider-owned. What it means
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`.
Source
Thrown at src/Azure/Orleans.Journaling.AzureStorage/AzureTableJournalStorage.cs:1177
}
private static void ValidateCallerMetadataProperty(string key, string value)
{
ValidateCallerMetadataPropertyName(key);
ArgumentNullException.ThrowIfNull(value);
}
private static void ValidateCallerMetadataPropertyName(string key)
{
ArgumentException.ThrowIfNullOrWhiteSpace(key);
if (key.IndexOf('\0') >= 0)
{
throw new ArgumentException("Journal metadata property names must not contain null characters.", nameof(key));
}
if (key.StartsWith("$", StringComparison.Ordinal))
{
throw new ArgumentException($"Journal metadata property '{key}' is provider-owned.", nameof(key));
}
}
private static ETag ToAzureETag(string eTag)
{
ArgumentException.ThrowIfNullOrWhiteSpace(eTag);
return new ETag(eTag);
}
private static bool IsEntityAlreadyExists(RequestFailedException exception)
=> exception.Status == 409
&& (string.Equals(exception.ErrorCode, "EntityAlreadyExists", StringComparison.Ordinal)
|| exception.Message.Contains("already exists", StringComparison.OrdinalIgnoreCase));
/// <summary>
/// Returns true when an Azure response indicates the journal header has been mutated since our
/// cached ETag was captured: HTTP 404 (header deleted), HTTP 412 (precondition failed / IfMatch
/// rejected), or HTTP 409 with <c>EntityAlreadyExists</c> (a competing writer already publishedView on GitHub (pinned to fca799fa70)
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.
Example fix
// before
storage.SetMetadata(new() { ["$kind"] = "foo" });
// after
storage.SetMetadata(new() { ["kind"] = "foo" }); Defensive patterns
Strategy: validation
Validate before calling
foreach (var k in metadata.Keys)
if (k.StartsWith('$')) throw new ArgumentException($"Key '{k}' is provider-owned"); Type guard
static bool IsCallerOwnedKey(string key) => !key.StartsWith('$', StringComparison.Ordinal); Prevention
- Reserve the '$' prefix for provider-owned fields.
- Use ':', '.', or '/' as namespace separators in caller metadata.
- Document the reserved prefix for callers.
When it happens
Trigger: Passing a metadata key beginning with '$' to a set/update/remove API; thrown at AzureTableJournalStorage.cs:1177.
Common situations: Caller used '$' as a namespace separator ('$namespace$field'); keys auto-prefixed with '$' by a framework; copied internal property names into caller metadata.
Related errors
- Journal metadata property '{propertyName}' cannot be both se
- Journal metadata property names must not contain null charac
- Journal metadata property names must not contain null charac
- Journal metadata property '{key}' is provider-owned.
- The journal id must not be the default value.
AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13).
Data as JSON: /api/errors/148da3f4277bf631.
Report an issue: GitHub.