dotnet/wpf · error · XamlObjectWriterException
SR.CannotSetBaseUri
Error message
SR.CannotSetBaseUri
What it means
XamlObjectWriterException thrown by Logic_CheckBaseUri when a BaseUri property assignment is attempted either after BaseUri was already set or on a non-root element (context depth greater than 2). BaseUri may only be assigned once, on the root element of the XAML stream.
Solutions
- Strip duplicate BaseUri/xml:base declarations from non-root elements before writing the node stream.
- Set BaseUri exactly once, only for the root object (Depth <= 2 in the writer).
- If merging documents, keep only the outermost document's base URI and drop inner ones.
- Guard the write site: check ctx.BaseUri is null and ctx.Depth <= 2 before emitting the BaseUri member.
Example fix
// before
WriteBaseUri(childObject, "file:///merged.xaml"); // non-root: throws
// after
if (ctx.BaseUri == null && ctx.Depth <= 2)
WriteBaseUri(rootObject, "file:///merged.xaml");
// else skip — nested fragments inherit the root base URI Defensive patterns
Strategy: validation
Validate before calling
if (ctx.BaseUri != null || ctx.Depth > 2)
return; // skip: BaseUri already set or not on root element Type guard
static bool CanSetBaseUri(ObjectWriterContext ctx) => ctx.BaseUri == null && ctx.Depth <= 2;
Try / catch
try { WriteBaseUri(obj, uri); }
catch (XamlObjectWriterException ex) when (ex.Message == System.Xaml.SR.CannotSetBaseUri) { LogDuplicateBaseUri(uri); } Prevention
- Emit xml:base only on the root element of the node stream.
- Strip BaseUri declarations from merged/inner XAML fragments.
- Ensure exactly one BaseUri assignment per document.
- Validate merged node streams for duplicate xml:base before writing.
When it happens
Trigger: Writing a second xml:base / BaseUri value into the node stream, or assigning BaseUri while the object writer sits deeper than the root element (Depth > 2, i.e. past the root's StartObject/StartMember slots).
Common situations: Merging or concatenating XAML fragments that each declare xml:base; templates/includes that re-declare base URI inside nested elements; custom node-stream transformers duplicating the BaseUri member on child objects.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- ' '.' ' is a property without a getter and is not a valid…
- ' '.' ' is a property without a getter and is not a valid…
- ' '.' ' is a property without a getter and is not a valid…
- ' ' is not a valid XAML member name.
- Animation_ChildMustBeKeyFrame
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/4df0ad8f8b12bf77.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/InfosetObjects/XamlObjectWriter.cs:1669
else if (prop == XamlLanguage.Base)
{
Logic_CheckBaseUri(ctx, (string)value);
ctx.BaseUri = new Uri((string)value);
if (ctx.ParentInstance is not null)
{
Runtime.SetUriBase(ctx.ParentType, ctx.ParentInstance, ctx.BaseUri);
}
}
}
}
private void Logic_CheckBaseUri(ObjectWriterContext ctx, string value)
{
// Make sure BaseUri is not already set and that we can only set BaseUri on the root element
// Depth > 2 because on the root element, SO/SM takes 1 slot, V is slot 2
if ((ctx.BaseUri is not null) || (ctx.Depth > 2))
{
throw new XamlObjectWriterException(SR.CannotSetBaseUri);
}
}
/// <summary>
/// Process the call to a MarkupExtension.ProvideValue() and assign the result
/// </summary>
private void Logic_AssignProvidedValue(ObjectWriterContext ctx)
{
bool handled = Logic_ProvideValue(ctx);
if (!handled && ctx.ParentProperty is not null)
{
Logic_DoAssignmentToParentProperty(ctx);
}
}
// Returns true if the assignment was handled by a SetMarkupExtensionHandler
private bool Logic_ProvideValue(ObjectWriterContext ctx)
{View on GitHub (pinned to 81131a70a4)