dotnet/orleans · error · ArgumentException

Journal metadata property names must not contain null…

Error message

Journal metadata property names must not contain null characters.

What it means

Thrown by AzureBlobJournalStorage when validating a caller-supplied metadata property name passed to Append/Replace/SetMetadata. The journal reserves certain metadata keys for its own bookkeeping (format, checkpoint, checkpoint_offset, wal_generation, and anything starting with '$'), and embedded NUL characters corrupt the Azure Blob metadata layer, so both are rejected up front before any write reaches the service.

Solutions

  1. Sanitize all caller metadata keys before passing them: strip NUL characters or reject them, e.g. key.Replace("\0", string.Empty).
  2. Restrict metadata keys to printable ASCII or UTF-8 identifiers without control characters.
  3. If you need binary metadata, put it in the metadata VALUE, not the key, or store it in the journal payload itself.

Example fix

// before
var meta = new Dictionary<string, string> { ["user\0id"] = "abc" };
await storage.AppendAsync(data, meta, ct);

// after
var meta = new Dictionary<string, string> { ["user_id"] = "abc" };
await storage.AppendAsync(data, meta, ct);
Defensive patterns

Strategy: validation

Validate before calling

static bool IsValidMetadataKey(string key)
    => !string.IsNullOrWhiteSpace(key)
        && key.IndexOf('\0') < 0
        && !key.StartsWith("$", StringComparison.Ordinal)
        && !IsReservedKey(key);

static bool IsReservedKey(string key)
    => key.Equals("format", StringComparison.OrdinalIgnoreCase)
        || key.Equals("checkpoint", StringComparison.OrdinalIgnoreCase)
        || key.Equals("checkpoint_offset", StringComparison.OrdinalIgnoreCase)
        || key.Equals("wal_generation", StringComparison.OrdinalIgnoreCase);

Prevention

When it happens

Trigger: Calling an IJournalStorage method that accepts IReadOnlyDictionary<string,string> metadata (e.g. AppendAsync, ReplaceAsync, CreateIfNotExistsAsync with metadata) and passing a dictionary whose key contains a '\0' character. Validation runs synchronously in ValidateCallerMetadataPropertyName before the first network call.

Common situations: Serializing a binary key or a struct value into a metadata key name; copying keys from an untrusted source without sanitization; using a key derived from a GUID or byte array that was not first encoded as a clean string.

Related errors


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

Appendix: source

Thrown at src/Azure/Orleans.Journaling.AzureStorage/AzureBlobJournalStorage.cs:1019

                changed = true;
            }
        }

        return changed;
    }

    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 (IsProviderMetadataKey(key))
        {
            throw new ArgumentException($"Journal metadata property '{key}' is provider-owned.", nameof(key));
        }
    }

    private static bool IsProviderMetadataKey(string key)
        => string.Equals(key, FormatMetadataKey, StringComparison.OrdinalIgnoreCase)
            || string.Equals(key, CheckpointMetadataKey, StringComparison.OrdinalIgnoreCase)
            || string.Equals(key, CheckpointOffsetMetadataKey, StringComparison.OrdinalIgnoreCase)
            || string.Equals(key, WalGenerationMetadataKey, StringComparison.OrdinalIgnoreCase)
            || key.StartsWith("$", StringComparison.Ordinal);

    private static ETag ToAzureETag(string eTag)
    {
        ArgumentException.ThrowIfNullOrWhiteSpace(eTag);

View on GitHub (pinned to fca799fa70)