[Windows] Fixed URI images not displaying - #31566
Conversation
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
There was a problem hiding this comment.
Pull Request Overview
This PR fixes an issue where URI-based images were not displaying on Windows due to HTTP requests lacking a User-Agent header, causing servers like Wikimedia to return 403 Forbidden errors.
- Adds Windows-specific User-Agent header to HTTP requests for image loading
- Implements P/Invoke call to Windows SDK to retrieve default User-Agent string
- Includes comprehensive UI test coverage for the fix
Reviewed Changes
Copilot reviewed 4 out of 8 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/Controls/src/Core/StreamWrapper.cs | Adds Windows-specific User-Agent header logic to HTTP requests |
| src/Controls/src/Core/Platform/Windows/Extensions/Extensions.cs | Implements Windows SDK P/Invoke method to obtain default User-Agent string |
| src/Controls/tests/TestCases.HostApp/Issues/Issue31363.cs | Creates UI test page with Wikimedia image to reproduce the issue |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31363.cs | Adds automated test to verify URI image loading works correctly |
| @@ -0,0 +1,24 @@ | |||
| namespace Maui.Controls.Sample.Issues; | |||
|
|
|||
| [Issue(IssueTracker.Github, 31363, "The images under the I-CollectionView category are not showing up", PlatformAffected.UWP)] | |||
There was a problem hiding this comment.
The platform should be PlatformAffected.WinUI instead of PlatformAffected.UWP since this is a Windows-specific fix for the modern WinUI platform, not the legacy UWP platform.
| [DllImport("urlmon.dll", ExactSpelling = true, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)] | ||
| private static extern int ObtainUserAgentString(int dwOption, StringBuilder userAgent, ref int length); |
There was a problem hiding this comment.
The P/Invoke declaration should include security attributes. Consider adding [SuppressUnmanagedCodeSecurity] if appropriate, or ensure proper security validation of the native method call.
| int result = ObtainUserAgentString(0, userAgentBuffer, ref length); | ||
|
|
||
| // Handle buffer overflow case - ObtainUserAgentString can return a longer string if needed | ||
| if (result == unchecked((int)0x8007000E)) // E_OUTOFMEMORY |
There was a problem hiding this comment.
The magic number 0x8007000E should be defined as a named constant (e.g., private const int E_OUTOFMEMORY = unchecked((int)0x8007000E);) to improve code readability and maintainability.
| { | ||
| try | ||
| { | ||
| const int maxPath = 260; |
There was a problem hiding this comment.
The magic number 260 should use the well-known constant System.IO.Path.MaxPath or define it as a named constant specific to user agent string length to clarify its purpose.
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 31566Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 31566" |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
/review -b feature/refactor-copilot-yml |
kubaflo
left a comment
There was a problem hiding this comment.
Can you please resolve conflicts?
|
/review -b feature/enhanced-reviewer -p windows |
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 2 findings
See inline comments for details.
| AutomationId = "TestImage", | ||
| Source = new UriImageSource | ||
| { | ||
| Uri = new Uri("https://upload.wikimedia.org/wikipedia/commons/thumb/1/13/Gelada-Pavian.jpg/320px-Gelada-Pavian.jpg") |
There was a problem hiding this comment.
[major] Regression Prevention / Test Reliability - This UI test depends on a live Wikimedia URL. Screenshot UI tests should be deterministic; network/CDN failures, redirects, remote content changes, or host policy changes can produce blank/different pixels unrelated to the Windows User-Agent fix. Please serve a stable in-repo image from a controlled local test endpoint, or use a test server/handler that verifies the User-Agent behavior and returns a fixed asset.
| public void UriImageSourceShouldDisplayProperly() | ||
| { | ||
| App.WaitForElement("TestImage"); | ||
| VerifyScreenshot(); |
There was a problem hiding this comment.
[moderate] Regression Prevention / Test Reliability - App.WaitForElement("TestImage") only waits for the Image view to exist; it does not wait for the URI image download/decode to complete. The screenshot can race and capture a blank placeholder, especially on slower Windows CI. Add a deterministic ready signal from the page and/or use VerifyScreenshot(retryTimeout: TimeSpan.FromSeconds(...)) after the image has finished loading.
kubaflo
left a comment
There was a problem hiding this comment.
Could you please check the ai's suggestions and failing tests?
|
/review rerun |
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 1 findings
See inline comments for details.
| WidthRequest = 60, | ||
| AutomationId = "TestImage", | ||
| Source = new UriImageSource | ||
| { |
There was a problem hiding this comment.
[major] Regression Prevention — This regression test depends on a third-party Wikimedia URL that is not reliable in the PR validation environment. The recorded gate still fails with the PR fix applied (UriImageSourceShouldDisplayProperly.png differs by 1.64%), and try-fix probing found the original URL returned HTTP 400 even with a User-Agent. Please switch this to a deterministic repo-controlled HTTPS asset, such as a commit-pinned raw.githubusercontent.com/dotnet/maui/... image, and refresh the affected snapshot baseline so the regression test actually validates the fix.
kubaflo
left a comment
There was a problem hiding this comment.
Could you please check the failing test?
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@Ahamed-Ali — new AI review results are available based on this last commit:
83946ad. To request a fresh review after new comments or commits, comment/review rerun.
🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix
Gate Result: ❌ FAILED
Platform: WINDOWS · Base: main · Merge base: 5ec887fa
🩺 Fix does not pass the tests — every test still fails after applying the fix. The PR's change does not resolve the failure(s).
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🖥️ Issue31363 Issue31363 |
✅ FAIL — 610s | ❌ FAIL — 469s |
🔴 Without fix — 🖥️ Issue31363: FAIL ✅ · 610s
Determining projects to restore...
Restored D:\a\1\s\src\Controls\src\Core\Controls.Core.csproj (in 50.11 sec).
Restored D:\a\1\s\src\Controls\Foldable\src\Controls.Foldable.csproj (in 1.24 sec).
Restored D:\a\1\s\src\Controls\Maps\src\Controls.Maps.csproj (in 52.06 sec).
Restored D:\a\1\s\src\BlazorWebView\src\Maui\Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 4.04 sec).
Restored D:\a\1\s\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (in 7 ms).
Restored D:\a\1\s\src\Essentials\src\Essentials.csproj (in 21 ms).
Restored D:\a\1\s\src\Core\src\Core.csproj (in 190 ms).
Restored D:\a\1\s\src\Core\maps\src\Maps.csproj (in 14 ms).
Restored D:\a\1\s\src\Graphics\src\Graphics\Graphics.csproj (in 3.86 sec).
Restored D:\a\1\s\src\Controls\src\Xaml\Controls.Xaml.csproj (in 22 ms).
Restored D:\a\1\s\src\Controls\tests\TestCases.HostApp\Controls.TestCases.HostApp.csproj (in 512 ms).
3 of 14 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.dll
Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Maps -> D:\a\1\s\artifacts\bin\Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Microsoft.AspNetCore.Components.WebView.Maui -> D:\a\1\s\artifacts\bin\Microsoft.AspNetCore.Components.WebView.Maui\Debug\net10.0-windows10.0.19041.0\Microsoft.AspNetCore.Components.WebView.Maui.dll
Controls.Foldable -> D:\a\1\s\artifacts\bin\Controls.Foldable\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Foldable.dll
Controls.Maps -> D:\a\1\s\artifacts\bin\Controls.Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Maps.dll
Controls.Xaml -> D:\a\1\s\artifacts\bin\Controls.Xaml\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Xaml.dll
Controls.TestCases.HostApp -> D:\a\1\s\artifacts\bin\Controls.TestCases.HostApp\Debug\net10.0-windows10.0.19041.0\win-x64\Controls.TestCases.HostApp.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:06:02.13
Determining projects to restore...
Restored D:\a\1\s\src\TestUtils\src\VisualTestUtils\VisualTestUtils.csproj (in 939 ms).
Restored D:\a\1\s\src\TestUtils\src\UITest.NUnit\UITest.NUnit.csproj (in 1.83 sec).
Restored D:\a\1\s\src\TestUtils\src\UITest.Core\UITest.Core.csproj (in 3 ms).
Restored D:\a\1\s\src\TestUtils\src\UITest.Appium\UITest.Appium.csproj (in 2 sec).
Restored D:\a\1\s\src\TestUtils\src\VisualTestUtils.MagickNet\VisualTestUtils.MagickNet.csproj (in 4.98 sec).
Restored D:\a\1\s\src\TestUtils\src\UITest.Analyzers\UITest.Analyzers.csproj (in 9.03 sec).
Restored D:\a\1\s\src\Controls\tests\CustomAttributes\Controls.CustomAttributes.csproj (in 13 ms).
Restored D:\a\1\s\src\Controls\tests\TestCases.WinUI.Tests\Controls.TestCases.WinUI.Tests.csproj (in 9.63 sec).
7 of 15 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Controls.CustomAttributes -> D:\a\1\s\artifacts\bin\Controls.CustomAttributes\Debug\net10.0\Controls.CustomAttributes.dll
Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0\Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0\Microsoft.Maui.dll
Controls.Core.Design -> D:\a\1\s\artifacts\bin\Controls.Core.Design\Debug\net472\Microsoft.Maui.Controls.DesignTools.dll
Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0\Microsoft.Maui.Controls.dll
UITest.Core -> D:\a\1\s\artifacts\bin\UITest.Core\Debug\net10.0\UITest.Core.dll
UITest.Appium -> D:\a\1\s\artifacts\bin\UITest.Appium\Debug\net10.0\UITest.Appium.dll
UITest.NUnit -> D:\a\1\s\artifacts\bin\UITest.NUnit\Debug\net10.0\UITest.NUnit.dll
VisualTestUtils -> D:\a\1\s\artifacts\bin\VisualTestUtils\Debug\netstandard2.0\VisualTestUtils.dll
VisualTestUtils.MagickNet -> D:\a\1\s\artifacts\bin\VisualTestUtils.MagickNet\Debug\netstandard2.0\VisualTestUtils.MagickNet.dll
UITest.Analyzers -> D:\a\1\s\artifacts\bin\UITest.Analyzers\Debug\netstandard2.0\UITest.Analyzers.dll
Controls.TestCases.WinUI.Tests -> D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
Test run for D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 6/25/2026 5:18:12 PM FixtureSetup for Issue31363(Windows)
>>>>> 6/25/2026 5:18:22 PM UriImageSourceShouldDisplayProperly Start
>>>>> 6/25/2026 5:18:37 PM UriImageSourceShouldDisplayProperly Stop
>>>>> 6/25/2026 5:18:38 PM Log types:
Failed UriImageSourceShouldDisplayProperly [16 s]
Error Message:
System.TimeoutException : Timed out waiting for element...
Stack Trace:
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
at Microsoft.Maui.TestCases.Tests.Issues.Issue31363.UriImageSourceShouldDisplayProperly() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31363.cs:line 17
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
NUnit Adapter 4.5.0.0: Test execution complete
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.11] Discovering: Controls.TestCases.WinUI.Tests
[xUnit.net 00:00:00.35] Discovered: Controls.TestCases.WinUI.Tests
Results File: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\Issue31363.trx
Total tests: 1
Failed: 1
Test Run Failed.
Total time: 1.0565 Minutes
>>> TRX_RESULT_FILE: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\Issue31363.trx
🟢 With fix — 🖥️ Issue31363: FAIL ❌ · 469s
Determining projects to restore...
All projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Essentials.dll
Graphics.Win2D -> D:\a\1\s\artifacts\bin\Graphics.Win2D\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Graphics.Win2D.WinUI.Desktop.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.dll
Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Microsoft.AspNetCore.Components.WebView.Maui -> D:\a\1\s\artifacts\bin\Microsoft.AspNetCore.Components.WebView.Maui\Debug\net10.0-windows10.0.19041.0\Microsoft.AspNetCore.Components.WebView.Maui.dll
Controls.Xaml -> D:\a\1\s\artifacts\bin\Controls.Xaml\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Xaml.dll
Maps -> D:\a\1\s\artifacts\bin\Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Maps.dll
Controls.Foldable -> D:\a\1\s\artifacts\bin\Controls.Foldable\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Foldable.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Controls.Maps -> D:\a\1\s\artifacts\bin\Controls.Maps\Debug\net10.0-windows10.0.19041.0\Microsoft.Maui.Controls.Maps.dll
Controls.TestCases.HostApp -> D:\a\1\s\artifacts\bin\Controls.TestCases.HostApp\Debug\net10.0-windows10.0.19041.0\win-x64\Controls.TestCases.HostApp.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:05:47.74
Determining projects to restore...
All projects are up-to-date for restore.
Controls.CustomAttributes -> D:\a\1\s\artifacts\bin\Controls.CustomAttributes\Debug\net10.0\Controls.CustomAttributes.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Graphics -> D:\a\1\s\artifacts\bin\Graphics\Debug\net10.0\Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Essentials -> D:\a\1\s\artifacts\bin\Essentials\Debug\net10.0\Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Core -> D:\a\1\s\artifacts\bin\Core\Debug\net10.0\Microsoft.Maui.dll
Controls.Core.Design -> D:\a\1\s\artifacts\bin\Controls.Core.Design\Debug\net472\Microsoft.Maui.Controls.DesignTools.dll
Controls.BindingSourceGen -> D:\a\1\s\artifacts\bin\Controls.BindingSourceGen\Debug\netstandard2.0\Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.90-ci+azdo.14486946
Controls.Core -> D:\a\1\s\artifacts\bin\Controls.Core\Debug\net10.0\Microsoft.Maui.Controls.dll
VisualTestUtils -> D:\a\1\s\artifacts\bin\VisualTestUtils\Debug\netstandard2.0\VisualTestUtils.dll
VisualTestUtils.MagickNet -> D:\a\1\s\artifacts\bin\VisualTestUtils.MagickNet\Debug\netstandard2.0\VisualTestUtils.MagickNet.dll
UITest.Core -> D:\a\1\s\artifacts\bin\UITest.Core\Debug\net10.0\UITest.Core.dll
UITest.NUnit -> D:\a\1\s\artifacts\bin\UITest.NUnit\Debug\net10.0\UITest.NUnit.dll
UITest.Appium -> D:\a\1\s\artifacts\bin\UITest.Appium\Debug\net10.0\UITest.Appium.dll
UITest.Analyzers -> D:\a\1\s\artifacts\bin\UITest.Analyzers\Debug\netstandard2.0\UITest.Analyzers.dll
Controls.TestCases.WinUI.Tests -> D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
Test run for D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in D:\a\1\s\artifacts\bin\Controls.TestCases.WinUI.Tests\Debug\net10.0\Controls.TestCases.WinUI.Tests.dll
NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 6/25/2026 5:26:16 PM FixtureSetup for Issue31363(Windows)
>>>>> 6/25/2026 5:26:25 PM UriImageSourceShouldDisplayProperly Start
>>>>> 6/25/2026 5:26:28 PM UriImageSourceShouldDisplayProperly Stop
>>>>> 6/25/2026 5:26:28 PM Log types:
Failed UriImageSourceShouldDisplayProperly [2 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: UriImageSourceShouldDisplayProperly.png (1.64% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at VisualTestUtils.VisualRegressionTester.Fail(String message) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 162
at VisualTestUtils.VisualRegressionTester.VerifyMatchesSnapshot(String name, ImageSnapshot actualImage, String environmentName, ITestContext testContext) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 123
at Microsoft.Maui.TestCases.Tests.UITest.<VerifyScreenshot>g__Verify|13_0(String name, <>c__DisplayClass13_0&) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 477
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance, Boolean includeTitleBar) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 309
at Microsoft.Maui.TestCases.Tests.Issues.Issue31363.UriImageSourceShouldDisplayProperly() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31363.cs:line 18
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
NUnit Adapter 4.5.0.0: Test execution complete
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.13] Discovering: Controls.TestCases.WinUI.Tests
[xUnit.net 00:00:00.35] Discovered: Controls.TestCases.WinUI.Tests
Results File: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\Issue31363.trx
Total tests: 1
Failed: 1
Test Run Failed.
Total time: 27.0683 Seconds
>>> TRX_RESULT_FILE: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\Issue31363.trx
⚠️ Failure Details
- ❌ Issue31363 FAILED with fix (should pass)
UriImageSourceShouldDisplayProperly [2 s]VisualTestUtils.VisualTestFailedException : Snapshot different than baseline: UriImageSourceShouldDisplayProperly.png (1.64% difference) If the correct baseline has changed (this isn't a a bug), th...
📁 Fix files reverted (2 files)
src/Controls/src/Core/Platform/Windows/Extensions/Extensions.cssrc/Controls/src/Core/StreamWrapper.cs
📱 UI Tests — CollectionView,Image
Detected UI test categories: CollectionView,Image
❌ Deep UI tests — 359 passed, 2 failed across 2 categories on platform-pool agent (replaces in-process counts above).
🧪 UI Test Execution Results (deep, platform pool)
| Category | Tests | Snapshot diffs |
|---|---|---|
CollectionView |
317/329 (1 ❌) | 2 diff PNGs |
Image |
42/45 (1 ❌) | 2 diff PNGs |
❌ CollectionView — 1 failed test
VerifyDefaultScrollToRequested
VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: VerifyDefaultScrollToRequested.png (0.52% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
at VisualTestUtils.VisualRegressionTester.Fail(String message) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 162
at VisualTestUtils.VisualRegressionTester.VerifyMatchesSnapshot(String name, ImageSnapshot actualImage, String environmentName, ITestContext testContext) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 123
at Microsoft.Maui.TestCases.Tests.UITest.<VerifyScreenshot>g__Verify|13_0(String name, <>c__DisplayClass13_0&) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 477
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 ret
...
❌ Image — 1 failed test
UriImageSourceShouldDisplayProperly
VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: UriImageSourceShouldDisplayProperly.png (1.64% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
at VisualTestUtils.VisualRegressionTester.Fail(String message) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 162
at VisualTestUtils.VisualRegressionTester.VerifyMatchesSnapshot(String name, ImageSnapshot actualImage, String environmentName, ITestContext testContext) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 123
at Microsoft.Maui.TestCases.Tests.UITest.<VerifyScreenshot>g__Verify|13_0(String name, <>c__DisplayClass13_0&) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 477
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`
...
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)
📋 Pre-Flight — Context & Validation
Issue: #31363 - The images under the I-CollectionView category are not showing up
PR: #31566 - Windows UriImageSource remote image User-Agent fix
Platforms Affected: Windows
Files Changed: 2 implementation, 6 test/snapshot
Key Findings
- PR adds a Windows-only default User-Agent for
UriImageSourceHTTP downloads when theHttpClienthas none. - The added UI regression test targets
Imageand uses a remote Wikimedia image. - GitHub CLI is unauthenticated in this environment, so PR narrative, comments, prior reviews, and required checks could not be queried.
- Expert review found the product direction sound, but the added test URL is currently invalid and can fail independently of the fix.
Code Review Summary
Verdict: NEEDS_CHANGES
Confidence: low
Errors: 1 | Warnings: 1 | Suggestions: 1
Key code review findings:
- ✗
src/Controls/tests/TestCases.HostApp/Issues/Issue31363.cs:17uses a Wikimedia thumbnail URL that currently returns HTTP 400 even with a User-Agent. - ⚠
src/Controls/tests/TestCases.HostApp/Issues/Issue31363.cs:3depends on the internet but is not markedisInternetRequired: true. - ℹ
src/Controls/src/Core/Platform/Windows/Extensions/Extensions.cs:95could cache the default Windows User-Agent to avoid repeated P/Invoke/allocation.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #31566 | Add Windows default User-Agent in StreamWrapper.GetStreamAsync by calling Platform.Extensions.GetDefaultWindowsUserAgent() and mutating HttpClient.DefaultRequestHeaders.UserAgent before download. |
❌ Gate failed before this phase | Extensions.cs, StreamWrapper.cs, UI test/snapshots |
Original PR; product direction appears sound but test URL is invalid. |
🔬 Code Review — Deep Analysis
Code Review — PR #31566
Independent Assessment
What this changes: Adds a Windows-only default User-Agent to UriImageSource HTTP downloads when no UA is already present, using urlmon.dll's ObtainUserAgentString. Adds a UI screenshot regression test for a remote UriImageSource.
Inferred motivation: Some servers, including Wikimedia/upload endpoints, reject .NET HttpClient image requests with no User-Agent, causing Windows remote images not to render.
Reconciliation with PR Narrative
Author claims: GitHub CLI was unauthenticated, so PR narrative/comments could not be read. The local PR delta and issue/test naming indicate this addresses #31363: remote images under the image/CollectionView UI area do not display on Windows.
Agreement/disagreement: The code matches the inferred goal of adding a UA for Windows image downloads. The regression test currently uses an invalid thumbnail URL, so it does not reliably validate that goal.
Prior Review Reconciliation
No prior ❌ Error findings found. Prior review surfaces could not be queried because gh is unauthenticated.
Blast Radius Assessment
- Runs for all instances: Yes, all Windows
Controls.UriImageSourcedownloads with emptyHttpClient.DefaultRequestHeaders.UserAgent. - Startup impact: No; only runs during image download.
- Static/shared state: No new static mutable state.
CI Status
- Required-check result: undetermined;
gh pr checkscould not be used because GitHub CLI is unauthenticated. - Classification: undetermined.
- Action taken: confidence capped; no PR comments posted.
Findings
❌ Error — Regression test uses a currently invalid thumbnail URL
src/Controls/tests/TestCases.HostApp/Issues/Issue31363.cs:17
The added test image URL currently returns HTTP 400 even when a User-Agent is supplied. This means the UI test can fail regardless of the product fix and does not reliably prove the no-User-Agent failure mode. A valid Wikimedia thumbnail variant such as 250px-Gelada-Pavian.jpg is suitable for the intended behavior: no UA -> rejected, UA -> success.
⚠️ Warning — Internet-dependent issue page is not marked as internet-required
src/Controls/tests/TestCases.HostApp/Issues/Issue31363.cs:3
The test page depends on a live external HTTP request, but the Issue attribute does not set isInternetRequired: true, unlike similar internet-backed issue tests.
💡 Suggestion — Consider caching the default Windows UA
src/Controls/src/Core/Platform/Windows/Extensions/Extensions.cs:95
GetDefaultWindowsUserAgent() P/Invokes and allocates on every Windows remote image download. Since the value is effectively process-level, a cached lazy value would avoid repeated native calls, especially for image-heavy CollectionViews.
Failure-Mode Probing
- Server rejects missing UA: Product approach should fix valid URLs; valid Wikimedia thumbnails reject no UA and accept requests with a UA.
- App/user already set UA: Preserved; code only adds default when
UserAgent.Count == 0. - URLMON fails/unavailable: Code logs and continues without UA; image may still fail, but no crash.
- Many images in CollectionView: Current caller creates a new
HttpClientper download, so header mutation is not racing, but repeated UA lookup is avoidable. - Added test URL: Currently fails independently of UA, so the regression test is not valid as written.
Verdict: NEEDS_CHANGES
Confidence: low overall due unauthenticated CI/prior-review gaps; high confidence in the invalid test URL finding.
Summary: The Windows product fix is directionally sound, but the added regression test currently points at an invalid Wikimedia thumbnail and can fail even with the fix. Update the test URL/dependency strategy before merge.
🛠️ Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix-1 | Request-scoped Windows User-Agent via HttpRequestMessage; cache URLMON UA; fix invalid internet UI test URL/metadata. |
✅ PASS | 4 files | Passed BuildAndRunHostApp.ps1 -Platform windows -TestFilter "FullyQualifiedName~Issue31363"; stronger than PR because it avoids mutating HttpClient.DefaultRequestHeaders. |
| PR | PR #31566 | Mutate client.DefaultRequestHeaders.UserAgent in StreamWrapper.GetStreamAsync using URLMON default UA; adds UI screenshot test. |
❌ Gate failed before this phase | 8 files | Directionally sound, but pre-flight found the added test URL is invalid and the test lacks internet metadata. |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| gpt-5.5 / maui-expert-reviewer | 1 | Yes | Generated request-scoped header approach with cached UA and test reliability corrections. |
Exhausted: No — stopped because Candidate #1 passed all executed tests and is demonstrably better than the PR's current fix.
Selected Fix: Candidate #1 — request-scoped User-Agent avoids persistent HttpClient default-header mutation, fixes the invalid regression-test URL, and passed the targeted Windows UI regression.
try-fix-1 — Request-Scoped Windows User-Agent
Approach Description
Use a Windows-only HttpRequestMessage in StreamWrapper.GetStreamAsync, attaching the fallback Windows User-Agent to that request only and using HttpClient.SendAsync. This differs from PR #31566's current fix, which mutates client.DefaultRequestHeaders.UserAgent before GetAsync.
Additional test reliability fixes included in this candidate:
- Mark the issue page as
isInternetRequired: true. - Change the Wikimedia test image from the invalid
320pxthumbnail to a valid120pxthumbnail. - Call
VerifyInternetConnectivity()before the screenshot assertion. - Cache the URLMON User-Agent lookup with
Lazy<string>.
Expert Review Guidance
The MAUI expert review identified this as the strongest alternative because request-scoped headers avoid shared-client side effects if StreamWrapper.GetStreamAsync is ever called with a reused HttpClient. Self-review findings were clean (reviewer-findings.json contains []).
Baseline Note
EstablishBrokenBaseline.ps1 could not establish a broken baseline because the worktree already contained unrelated uncommitted changes outside this PR. Per autonomous execution rules, this phase continued from the current PR state and did not modify or discard those unrelated changes.
╔═══════════════════════════════════════════════════════════════════╗
║ ERROR: DIRTY WORKING DIRECTORY - Cannot establish baseline ║
╚═══════════════════════════════════════════════════════════════════╝
The following files have uncommitted changes:
M .github/scripts/BuildAndRunHostApp.ps1
M .github/scripts/Find-RegressionRisks.ps1
M .github/scripts/Invoke-RerunReviewTrigger.Tests.ps1
M .github/scripts/Invoke-RerunReviewTrigger.ps1
M .github/scripts/Post-AISummaryComment.Tests.ps1
M .github/scripts/Query-RerunReadyPRs.ps1
M .github/scripts/Resolve-RerunEligibility.Tests.ps1
M .github/scripts/Resolve-RerunEligibility.ps1
M .github/scripts/Review-PR.Tests.ps1
M .github/scripts/Review-PR.ps1
M .github/scripts/Review-Tests.ps1
M .github/scripts/post-ai-summary-comment.ps1
M .github/scripts/post-inline-review.ps1
M .github/scripts/shared/Update-AgentLabels.ps1
M .github/scripts/shared/shared-utils.ps1
M .github/skills/azdo-build-investigator/SKILL.md
M .github/skills/release-readiness/SKILL.md
M .github/skills/release-readiness/scripts/Get-PreviewReadiness.ps1
M .github/skills/release-readiness/scripts/Get-ReleaseReadiness.ps1
D .github/skills/release-readiness/scripts/NightlyFeed.ps1
M .github/skills/release-readiness/tests/Test-ReleaseReadiness.ps1
M .github/skills/review-test-failures/SKILL.md
M .github/skills/review-test-failures/scripts/Gather-TestFailureContext.ps1
M .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1
M .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1
M eng/scripts/detect-ui-test-categories.ps1
M eng/scripts/get-maui-pr.ps1
M eng/scripts/get-maui-pr.sh
This usually means a previous try-fix attempt did not restore properly.
To fix:
1. Run 'git status' to review the changes
2. Either commit them: git add . && git commit -m 'Save changes'
3. Or discard them: git checkout -- .
4. Then retry this script
Exception: D:\a\1\s\.github\scripts\EstablishBrokenBaseline.ps1:387
Line |
387 | throw "EstablishBrokenBaseline.ps1 failed: Working directory is n …
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| EstablishBrokenBaseline.ps1 failed: Working directory is not clean. Clean up before establishing baseline.
baseline exit code: 1
Diff
diff --git a/src/Controls/src/Core/Platform/Windows/Extensions/Extensions.cs b/src/Controls/src/Core/Platform/Windows/Extensions/Extensions.cs
index 3a65d0e7f6..95f5fc6487 100644
--- a/src/Controls/src/Core/Platform/Windows/Extensions/Extensions.cs
+++ b/src/Controls/src/Core/Platform/Windows/Extensions/Extensions.cs
@@ -92,7 +92,13 @@ namespace Microsoft.Maui.Controls.Platform
[DllImport("urlmon.dll", ExactSpelling = true, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern int ObtainUserAgentString(int dwOption, StringBuilder userAgent, ref int length);
- internal static string GetDefaultWindowsUserAgent()
+ static readonly Lazy<string> s_defaultWindowsUserAgent =
+ new(GetDefaultWindowsUserAgentUncached, LazyThreadSafetyMode.ExecutionAndPublication);
+
+ internal static string GetDefaultWindowsUserAgent() =>
+ s_defaultWindowsUserAgent.Value;
+
+ static string GetDefaultWindowsUserAgentUncached()
{
try
{
diff --git a/src/Controls/src/Core/StreamWrapper.cs b/src/Controls/src/Core/StreamWrapper.cs
index 770938553f..0620bbb3cb 100644
--- a/src/Controls/src/Core/StreamWrapper.cs
+++ b/src/Controls/src/Core/StreamWrapper.cs
@@ -93,16 +93,21 @@ namespace Microsoft.Maui.Controls
public static async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken, HttpClient client)
{
#if WINDOWS
+ using var request = new HttpRequestMessage(HttpMethod.Get, uri);
+
if (client.DefaultRequestHeaders.UserAgent.Count == 0)
{
var userAgent = Platform.Extensions.GetDefaultWindowsUserAgent();
- if (!string.IsNullOrEmpty(userAgent))
+ if (!string.IsNullOrWhiteSpace(userAgent))
{
- client.DefaultRequestHeaders.UserAgent.ParseAdd(userAgent);
+ request.Headers.TryAddWithoutValidation("User-Agent", userAgent);
}
}
-#endif
+
+ var response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false);
+#else
var response = await client.GetAsync(uri, cancellationToken).ConfigureAwait(false);
+#endif
if (!response.IsSuccessStatusCode)
{
Application.Current?.FindMauiContext()?.CreateLogger<StreamWrapper>()?
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31363.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31363.cs
index 0b7fe972d0..ee41965275 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue31363.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31363.cs
@@ -1,6 +1,6 @@
namespace Maui.Controls.Sample.Issues;
-[Issue(IssueTracker.Github, 31363, "The images under the I-CollectionView category are not showing up", PlatformAffected.UWP)]
+[Issue(IssueTracker.Github, 31363, "The images under the I-CollectionView category are not showing up", PlatformAffected.UWP, isInternetRequired: true)]
public class Issue31363 : ContentPage
{
public Issue31363()
@@ -14,7 +14,7 @@ public class Issue31363 : ContentPage
AutomationId = "TestImage",
Source = new UriImageSource
{
- Uri = new Uri("https://upload.wikimedia.org/wikipedia/commons/thumb/1/13/Gelada-Pavian.jpg/320px-Gelada-Pavian.jpg")
+ Uri = new Uri("https://upload.wikimedia.org/wikipedia/commons/thumb/1/13/Gelada-Pavian.jpg/120px-Gelada-Pavian.jpg")
}
};
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31363.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31363.cs
index dd1714706a..2a35bef3c2 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31363.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31363.cs
@@ -14,6 +14,7 @@ public class Issue31363 : _IssuesUITest
[Category(UITestCategories.Image)]
public void UriImageSourceShouldDisplayProperly()
{
+ VerifyInternetConnectivity();
App.WaitForElement("TestImage");
VerifyScreenshot();
}Test Results
Result: PASS
Command: pwsh .github\scripts\BuildAndRunHostApp.ps1 -Platform windows -TestFilter "FullyQualifiedName~Issue31363"
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 55.7766 Seconds
>>> TRX_RESULT_FILE: D:\a\1\s\CustomAgentLogsTmp\UITests\TestResults\FullyQualifiedName_Issue31363.trx
ℹ️ Test output saved to: D:\a\1\s\CustomAgentLogsTmp\UITests\test-output.log
🔹 Collecting test artifacts (screenshots, page source)...
ℹ️ Test artifacts collected: 0 screenshot(s), 0 page source(s) (copied 0 from assembly dir)
🔹 Capturing device logs...
ℹ️ Windows platform - logs captured from test output
ℹ️ Windows device log created: D:\a\1\s\CustomAgentLogsTmp\UITests\windows-device.log
═══════════════════════════════════════════════════════
Windows App Logs (Last 100 lines)
═══════════════════════════════════════════════════════
Windows UI Test run at 06/25/2026 17:45:49
ℹ️ Full device log: D:\a\1\s\CustomAgentLogsTmp\UITests\windows-device.log
═══════════════════════════════════════════════════════
✅ All tests passed
╔═══════════════════════════════════════════════════════════╗
║ Test Summary ║
╠═══════════════════════════════════════════════════════════╣
║ Platform: WINDOWS ║
║ Device: host ║
║ Test Filter: FullyQualifiedName~Issue31363 ║
║ Result: SUCCESS ✅ ║
║ Logs: D:\a\1\s\CustomAgentLogsTmp\UITests
╚═══════════════════════════════════════════════════════════╝
Failure Analysis
No failure. The Windows HostApp built successfully and the targeted WinUI UI test UriImageSourceShouldDisplayProperly passed. This candidate is demonstrably better than the PR's current fix because it preserves the behavior fix while avoiding mutation of HttpClient.DefaultRequestHeaders, fixes the invalid regression-test URL, and marks the internet-dependent test correctly.
📝 Recommended PR Title & Description
Assessment: ✏️ Recommend updating — the current metadata describes the raw PR approach but not the winning request-scoped header fix or the test reliability corrections.
Recommended title
[Windows] UriImageSource: Add request-scoped default User-Agent
Recommended description
### Root Cause of the issue
- On Windows, URI image loading uses `System.Net.Http.HttpClient` in `StreamWrapper.GetStreamAsync`, which does not automatically include a User-Agent header in HTTP requests. Many web servers (such as Wikimedia) block requests without a User-Agent header as an anti-bot security measure. As a result, image loading fails on Windows with a 403 Forbidden error, and the image is not displayed.
### Description of Change
- Retrieve the default Windows User-Agent string from URLMON/Windows SDK behavior for HTTP image requests.
- Apply the fallback User-Agent on the individual `HttpRequestMessage` used by `StreamWrapper.GetStreamAsync` instead of mutating `HttpClient.DefaultRequestHeaders`, avoiding shared-client side effects and header leakage to future requests.
- Cache the default Windows User-Agent lookup so repeated image downloads do not repeatedly call the native API.
- Add UI regression coverage for `UriImageSource` and make the internet-dependent test reliable by using a valid Wikimedia thumbnail URL, marking the issue with `isInternetRequired: true`, and checking `VerifyInternetConnectivity()` before the screenshot assertion.
### Reference
- [DefaultUserAgent](https://github.com/dotnet/wpf/blob/09a6800ac25e40fc636d22a3e78216840b386ba2/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/WpfWebRequestHelper.cs#L143)
- [Implementation](https://github.com/dotnet/wpf/blob/main/src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/UnsafeNativeMethodsOther.cs#L233)
### Issues Fixed
Fixes #31363
### Tested the behaviour in the following platforms
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
### Screenshot
| Before Issue Fix | After Issue Fix |
|----------|----------|
| <video src="https://github.com/user-attachments/assets/6d69db09-97d8-4be6-beeb-eb64c67f7cc0"> | <video src="https://github.com/user-attachments/assets/19b33589-5444-4d2a-9ca1-461fdcbbff84"> |
🏁 Report — Final Recommendation
Comparative Report — PR #31566
Candidates compared
| Rank | Candidate | Regression result | Assessment |
|---|---|---|---|
| 1 | try-fix-1 |
✅ PASS | Best supported candidate. It uses a request-scoped Windows User-Agent, caches URLMON lookup, fixes the invalid test URL, and adds internet gating. It passed BuildAndRunHostApp.ps1 -Platform windows -TestFilter "FullyQualifiedName~Issue31363". |
| 2 | pr-plus-reviewer |
Inferred ✅ (same material changes as try-fix-1) |
Technically equivalent to the winning approach after applying expert feedback in a sandbox copy, but it was not independently re-run as a separate candidate in STEP 5a. |
| 3 | pr |
❌ FAILED | Raw PR direction is valid, but the gate failed after applying the PR fix, the implementation mutates shared HttpClient.DefaultRequestHeaders, and the added UI test uses an invalid external thumbnail without internet gating. |
Analysis
The raw PR correctly identifies that Windows HTTP image loading may need a User-Agent to avoid 403 responses from some servers, but it applies that header by mutating the supplied HttpClient defaults. That is riskier than necessary because DefaultRequestHeaders is shared state: callers may reuse the client, concurrent downloads may observe mutation, and future requests can inherit a MAUI-injected User-Agent unexpectedly.
try-fix-1 preserves the product fix while narrowing the header to the single image request. It also addresses the test failures found in pre-flight and expert review: the original 320px Wikimedia URL is invalid, and the internet-dependent test needs the same connectivity handling used by other MAUI UI tests. Because try-fix-1 passed the targeted Windows UI regression and the raw PR failed, the failed raw PR must rank lower.
Winner
Winner: try-fix-1
Rationale: It is the only candidate with direct passing regression evidence and it incorporates all expert-review corrections: request-scoped User-Agent, cached URLMON lookup, valid regression URL, and internet-aware UI test metadata. pr-plus-reviewer is technically aligned but has only sandbox/inferred evidence, while the raw PR failed the gate and retains actionable review issues.
🧭 Next Steps — alternative fix proposed (try-fix-1)
Automated review — alternative fix proposed
The expert-reviewer evaluation compared the PR fix against automatically generated candidates and selected try-fix-1 as the strongest fix.
Why: try-fix-1 wins because it is the only candidate with direct passing Windows regression evidence and it incorporates the expert-review corrections: request-scoped User-Agent, cached URLMON lookup, valid test URL, and internet gating. The raw PR failed the gate and retains shared HttpClient header mutation/test reliability issues.
Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.
Candidate diff (try-fix-1)
diff --git a/src/Controls/src/Core/Platform/Windows/Extensions/Extensions.cs b/src/Controls/src/Core/Platform/Windows/Extensions/Extensions.cs
index b3af4366ff..95f5fc6487 100644
--- a/src/Controls/src/Core/Platform/Windows/Extensions/Extensions.cs
+++ b/src/Controls/src/Core/Platform/Windows/Extensions/Extensions.cs
@@ -1,8 +1,11 @@
#nullable disable
using System;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
using Microsoft.Maui.Controls.Internals;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -85,5 +88,44 @@ namespace Microsoft.Maui.Controls.Platform
return UwpScrollBarVisibility.Auto;
}
}
+
+ [DllImport("urlmon.dll", ExactSpelling = true, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true)]
+ private static extern int ObtainUserAgentString(int dwOption, StringBuilder userAgent, ref int length);
+
+ static readonly Lazy<string> s_defaultWindowsUserAgent =
+ new(GetDefaultWindowsUserAgentUncached, LazyThreadSafetyMode.ExecutionAndPublication);
+
+ internal static string GetDefaultWindowsUserAgent() =>
+ s_defaultWindowsUserAgent.Value;
+
+ static string GetDefaultWindowsUserAgentUncached()
+ {
+ try
+ {
+ const int maxPath = 260;
+ int length = maxPath;
+ var userAgentBuffer = new StringBuilder(length);
+ int result = ObtainUserAgentString(0, userAgentBuffer, ref length);
+
+ // Handle buffer overflow case - ObtainUserAgentString can return a longer string if needed
+ if (result == unchecked((int)0x8007000E)) // E_OUTOFMEMORY
+ {
+ userAgentBuffer = new StringBuilder(length);
+ result = ObtainUserAgentString(0, userAgentBuffer, ref length);
+ }
+
+ if (result >= 0) // SUCCEEDED(result)
+ {
+ return userAgentBuffer.ToString();
+ }
+ }
+ catch (Exception ex)
+ {
+ Application.Current?.FindMauiContext()?.CreateLogger<StreamWrapper>()?
+ .LogWarning("Failed to obtain Default Windows User-Agent string: {Exception}", ex.Message);
+ }
+
+ return null;
+ }
}
}
\ No newline at end of file
diff --git a/src/Controls/src/Core/StreamWrapper.cs b/src/Controls/src/Core/StreamWrapper.cs
index 44b5662a33..dbdbe2d693 100644
--- a/src/Controls/src/Core/StreamWrapper.cs
+++ b/src/Controls/src/Core/StreamWrapper.cs
@@ -92,7 +92,22 @@ namespace Microsoft.Maui.Controls
public static async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken, HttpClient client)
{
+#if WINDOWS
+ using var request = new HttpRequestMessage(HttpMethod.Get, uri);
+
+ if (client.DefaultRequestHeaders.UserAgent.Count == 0)
+ {
+ var userAgent = Platform.Extensions.GetDefaultWindowsUserAgent();
+ if (!string.IsNullOrWhiteSpace(userAgent))
+ {
+ request.Headers.TryAddWithoutValidation("User-Agent", userAgent);
+ }
+ }
+
+ var response = await client.SendAsync(request, cancellationToken).ConfigureAwait(false);
+#else
var response = await client.GetAsync(uri, cancellationToken).ConfigureAwait(false);
+#endif
if (!response.IsSuccessStatusCode)
{
Application.Current?.FindMauiContext()?.CreateLogger<StreamWrapper>()?
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue31363.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue31363.cs
new file mode 100644
index 0000000000..890e4f7ef3
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue31363.cs
@@ -0,0 +1,24 @@
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 31363, "The images under the I-CollectionView category are not showing up", PlatformAffected.UWP, isInternetRequired: true)]
+public class Issue31363 : ContentPage
+{
+ public Issue31363()
+ {
+ var layout = new VerticalStackLayout();
+
+ var image = new Image
+ {
+ HeightRequest = 60,
+ WidthRequest = 60,
+ AutomationId = "TestImage",
+ Source = new UriImageSource
+ {
+ Uri = new Uri("https://upload.wikimedia.org/wikipedia/commons/thumb/1/13/Gelada-Pavian.jpg/120px-Gelada-Pavian.jpg")
+ }
+ };
+
+ layout.Children.Add(image);
+ Content = layout;
+ }
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31363.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31363.cs
new file mode 100644
index 0000000000..2a35bef3c2
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue31363.cs
@@ -0,0 +1,21 @@
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue31363 : _IssuesUITest
+{
+ public Issue31363(TestDevice device) : base(device) { }
+
+ public override string Issue => "The images under the I-CollectionView category are not showing up";
+
+ [Test]
+ [Category(UITestCategories.Image)]
+ public void UriImageSourceShouldDisplayProperly()
+ {
+ VerifyInternetConnectivity();
+ App.WaitForElement("TestImage");
+ VerifyScreenshot();
+ }
+}
\ No newline at end of file
kubaflo
left a comment
There was a problem hiding this comment.
Could you please update snapshots?
|
Closing as a stale one - please open a new one if this PR is still needed |
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Root Cause of the issue
GetStreamAsyncmethod of the StreamWrapper class, which does not automatically include a User-Agent header in HTTP requests. Many web servers (such as Wikimedia) block requests without a User-Agent header as an anti-bot security measure. As a result, image loading fails on Windows with a 403 Forbidden error, and the image is not displayedDescription of Change
User-Agentstring retrieval from the Windows SDK for HTTP requests, resolving 403 Forbidden errors from servers (e.g., Wikimedia) that block requests without User-Agent headers.Reference
Issues Fixed
Fixes #31363
Tested the behaviour in the following platforms
Screenshot
UriImageNotShowing.mp4
UriImageShowing.mp4