Issue Description
When using MAUI.Sherpa's Inspector to modify element properties, setting any property value on a selected element results in an an unhandled error has occured error
Steps to Reproduce
-
Create a new .NET MAUI app using the default template (with "Hello, World!" label and counter button)
-
Add the DevFlow Agent to the app:
#if DEBUG
builder.Logging.AddDebug();
builder.AddMauiDevFlowAgent();
#endif
-
run the app on Windows
-
Open MAUI.Sherpa and connect the app inspector to the running app
-
In the visual tree, click on any element (e.g., the "Hello, World!" Label)
-
In the Properties pane on the right, change any property value (e.g., FontSize, Text, etc.)
-
Result: Unhandled Blazor exception appears
Environment
MAUI.Sherpa Version: v0.15.0
DevFlow Agent Version: 0.1.0-preview.12.26421.1
.NET MAUI Version: .NET MAUI 11 Preview 7
Platform: Windows (Sherpa and inspected app both running on Windows)
Other platforms: Not tested yet; Android/iOS testing planned.
Error Details:
Response status code does not indicate success: 409 (Conflict).
at System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
at MauiSherpa.Core.Services.DevFlowV1Client.SetPropertyAsync(String elementId, String propertyName, Object value, CancellationToken ct)
at MauiSherpa.AppInspector.Services.InspectorUiClient.SetPropertyAsync(String elementId, String propertyName, String value, CancellationToken ct)
at MauiSherpa.AppInspector.Pages.Inspector.DevFlowTreeTab.<>c__DisplayClass99_0.<<RenderEnumEditor>b__1>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
Suspected Root Cause
The DevFlow agent validates capture epochs/generations when performing UI mutations. The current Sherpa implementation does not appear to pass the capture metadata obtained from GetTreeAsync() to SetPropertyAsync().
The suspected flow is:
- Sherpa calls
GetTreeAsync() to capture the visual tree and receives capture metadata including an epoch/generation.
- Sherpa does not appear to cache/pass this metadata when subsequently calling
SetPropertyAsync().
SetPropertyAsync() in DevFlowV1Client currently sends only the property value, with no capture epoch/generation:
var response = await _http.PutAsJsonAsync(url, new { value }, JsonOptions, ct);
- The DevFlow agent's
HandleSetProperty() calls PrepareUiMutationAsync(), which validates the capture state.
- Based on the DevFlow agent code, this validation may reject the mutation when the capture epoch is considered stale, resulting in a
409 Conflict (potentially with a stale-capture-epoch reason).
- The HTTP error is not handled by Sherpa and ultimately surfaces as an unhandled Blazor exception.
Notably, the 409 occurs even when the app has not been interacted with or visibly changed after connecting Sherpa. Refreshing/reloading the Inspector tree immediately before changing the property also does not prevent the 409. This suggests that an actual UI mutation between tree capture and property mutation is unlikely to be the sole cause.
Expected Behavior
Setting property values should succeed without an HTTP 409 error.
If the mutation cannot be applied because the Inspector capture is stale or otherwise invalid, Sherpa should handle the conflict gracefully instead of surfacing an unhandled Blazor exception.
Additional Context
-
This reproduces consistently for me.
-
The 409 occurs even when the app has not been interacted with or visibly changed after connecting Sherpa.
-
Refreshing/reloading the Inspector tree immediately before changing the property does not prevent the 409.
-
Related code:
-
SetPropertyAsync in DevFlowV1Client:
|
public async Task<object?> SetPropertyAsync(string elementId, string propertyName, object value, CancellationToken ct = default) |
|
{ |
|
var url = $"/api/v1/ui/elements/{Uri.EscapeDataString(elementId)}/properties/{Uri.EscapeDataString(propertyName)}"; |
|
var response = await _http.PutAsJsonAsync(url, new { value }, JsonOptions, ct); |
|
response.EnsureSuccessStatusCode(); |
|
var result = await response.Content.ReadFromJsonAsync<JsonElement>(JsonOptions, ct); |
|
return result.TryGetProperty("value", out var val) ? val.Deserialize<object>(JsonOptions) : null; |
|
} |
-
RenderEnumEditor in DevFlowTreeTab.razor:
|
__builder.AddAttribute(8, "onchange", EventCallback.Factory.Create<ChangeEventArgs>(this, async (e) => |
|
{ |
|
if (selectedElement != null) |
|
{ |
|
await Client.SetPropertyAsync(selectedElement.Id, prop.Name, e.Value?.ToString() ?? ""); |
|
await RefreshTree(); |
|
await RefreshProperties(); |
|
} |
|
})); |
|
|
|
if (prop.Meta.EnumValues != null) |
|
{ |
|
foreach (var val in prop.Meta.EnumValues) |
|
{ |
|
__builder.OpenElement(10, "option"); |
|
__builder.AddAttribute(11, "value", val); |
|
if (val.Equals(prop.Value, StringComparison.OrdinalIgnoreCase)) |
|
__builder.AddAttribute(12, "selected", true); |
|
__builder.AddContent(13, val); |
|
__builder.CloseElement(); |
|
} |
|
} |
|
__builder.CloseElement(); // select |
|
__builder.CloseElement(); // div |
|
}; |
|
|
|
private RenderFragment RenderColorEditor(PropertyEntry prop) => __builder => |
|
{ |
|
var cssColor = MauiColorToCss(prop.Value); |
-
CommitEdit in DevFlowTreeTab.razor:
|
var prop = editingProperty; |
|
var val = editValue; |
|
var elementId = selectedElement.Id; |
|
|
|
// Cache CSS value for brush properties (MAUI ToString doesn't preserve gradient stops) |
|
var meta = GetMeta(prop); |
|
if (meta.Editor == EditorType.Brush && !string.IsNullOrEmpty(val)) |
|
{ |
|
brushValueCache[$"{elementId}:{prop}"] = val; |
|
} |
|
|
|
editingProperty = null; |
|
gradientStops.Clear(); |
|
selectedStopIndex = null; |
|
isDraggingStop = false; |
|
StateHasChanged(); |
|
await Client.SetPropertyAsync(elementId, prop, val); |
|
await RefreshTree(); |
|
await RefreshProperties(); |
Issue Description
When using MAUI.Sherpa's Inspector to modify element properties, setting any property value on a selected element results in an an unhandled error has occured error
Steps to Reproduce
Create a new .NET MAUI app using the default template (with "Hello, World!" label and counter button)
Add the DevFlow Agent to the app:
run the app on Windows
Open MAUI.Sherpa and connect the app inspector to the running app
In the visual tree, click on any element (e.g., the "Hello, World!" Label)
In the Properties pane on the right, change any property value (e.g., FontSize, Text, etc.)
Result: Unhandled Blazor exception appears
Environment
MAUI.Sherpa Version: v0.15.0
DevFlow Agent Version: 0.1.0-preview.12.26421.1
.NET MAUI Version: .NET MAUI 11 Preview 7
Platform: Windows (Sherpa and inspected app both running on Windows)
Other platforms: Not tested yet; Android/iOS testing planned.
Error Details:
Suspected Root Cause
The DevFlow agent validates capture epochs/generations when performing UI mutations. The current Sherpa implementation does not appear to pass the capture metadata obtained from
GetTreeAsync()toSetPropertyAsync().The suspected flow is:
GetTreeAsync()to capture the visual tree and receives capture metadata including an epoch/generation.SetPropertyAsync().SetPropertyAsync()inDevFlowV1Clientcurrently sends only the property value, with no capture epoch/generation:var response = await _http.PutAsJsonAsync(url, new { value }, JsonOptions, ct);HandleSetProperty()callsPrepareUiMutationAsync(), which validates the capture state.409 Conflict(potentially with astale-capture-epochreason).Notably, the 409 occurs even when the app has not been interacted with or visibly changed after connecting Sherpa. Refreshing/reloading the Inspector tree immediately before changing the property also does not prevent the 409. This suggests that an actual UI mutation between tree capture and property mutation is unlikely to be the sole cause.
Expected Behavior
Setting property values should succeed without an HTTP 409 error.
If the mutation cannot be applied because the Inspector capture is stale or otherwise invalid, Sherpa should handle the conflict gracefully instead of surfacing an unhandled Blazor exception.
Additional Context
This reproduces consistently for me.
The 409 occurs even when the app has not been interacted with or visibly changed after connecting Sherpa.
Refreshing/reloading the Inspector tree immediately before changing the property does not prevent the 409.
Related code:
SetPropertyAsync in DevFlowV1Client:
MAUI.Sherpa/src/MauiSherpa.Core/Services/Inspector/DevFlowV1Client.cs
Lines 134 to 141 in a58638e
RenderEnumEditor in DevFlowTreeTab.razor:
MAUI.Sherpa/src/MauiSherpa.AppInspector/Pages/Inspector/DevFlowTreeTab.razor
Lines 1516 to 1544 in a58638e
CommitEdit in DevFlowTreeTab.razor:
MAUI.Sherpa/src/MauiSherpa.AppInspector/Pages/Inspector/DevFlowTreeTab.razor
Lines 1357 to 1375 in a58638e