dotnet/orleans · error · ArgumentException
Journal metadata property '{propertyName}' cannot be both se
Error message
Journal metadata property '{propertyName}' cannot be both set and removed. What it means
A metadata update passed the same property name in both the set dictionary and the remove set. CopyRemove (AzureTableJournalStorage.cs:1115) validates each removed key against the set dictionary and rejects the contradiction because the net effect would be ambiguous. The argument blamed is `remove`.
Source
Thrown at src/Azure/Orleans.Journaling.AzureStorage/AzureTableJournalStorage.cs:1128
}
return result;
}
private static IReadOnlySet<string> CopyRemove(IEnumerable<string>? remove, IReadOnlyDictionary<string, string> set)
{
if (remove is null)
{
return new HashSet<string>(StringComparer.Ordinal);
}
var result = new HashSet<string>(StringComparer.Ordinal);
foreach (var propertyName in remove)
{
ValidateCallerMetadataPropertyName(propertyName);
if (set.ContainsKey(propertyName))
{
throw new ArgumentException($"Journal metadata property '{propertyName}' cannot be both set and removed.", nameof(remove));
}
result.Add(propertyName);
}
return result;
}
private static bool ApplyCallerMetadataUpdate(
Dictionary<string, string> metadata,
IReadOnlyDictionary<string, string> set,
IReadOnlySet<string> remove)
{
var changed = false;
foreach (var propertyName in remove)
{
changed |= metadata.Remove(propertyName);
}View on GitHub (pinned to fca799fa70)
Solutions
- Disjoint the keys: remove a name from set before passing it in remove, or vice versa.
- Decide per-key intent (set wins vs remove wins) and filter the other collection before the call.
- Add a precondition check in calling code: `set.Keys.Intersect(remove).Any()` should be false.
Example fix
// before
storage.UpdateMetadata(set: new() { ["k"] = "v" }, remove: new[] { "k" });
// after
var set = new Dictionary<string, string> { ["k"] = "v" };
var remove = new[] { "other" };
storage.UpdateMetadata(set: set, remove: remove); Defensive patterns
Strategy: validation
Validate before calling
var overlap = set.Keys.Intersect(remove, StringComparer.Ordinal).ToList();
if (overlap.Count > 0) throw new ArgumentException($"Keys both set and removed: {string.Join(", ", overlap)}"); Type guard
static bool IsMetadataUpdateDisjoint(
IReadOnlyDictionary<string,string> set, IEnumerable<string> remove)
=> !remove.Any(r => set.ContainsKey(r)); Prevention
- Build set and remove from a single source of truth so a key has one intent.
- Assert disjointness in a precondition check.
- Unit-test metadata-diff logic for overlap.
When it happens
Trigger: Calling a journal metadata-update API (e.g., the method that calls CopyRemove) with overlapping keys in its set and remove parameters; thrown at AzureTableJournalStorage.cs:1128.
Common situations: Caller builds set and remove from the same source list; a UI applies a diff that marks a key as both changed and deleted; copying a key collection into both arguments by mistake.
Related errors
- Journal metadata property names must not contain null charac
- Journal metadata property '{key}' is provider-owned.
- 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/cbbfcc2ed1263e56.
Report an issue: GitHub.