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 published

View on GitHub (pinned to fca799fa70)

Solutions

  1. Use a different separator (':', '.', '/') in caller metadata keys.
  2. Strip a leading '$' before the call if it is decorative.
  3. 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

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


AI-assisted analysis of dotnet/orleans@fca799fa70 (2026-08-13). Data as JSON: /api/errors/148da3f4277bf631. Report an issue: GitHub.