Skip to content

[WIP][Windows] Fix Entry.Completed firing during IME candidate confirmation#36577

Draft
HarishwaranVijayakumar wants to merge 3 commits into
dotnet:mainfrom
HarishwaranVijayakumar:fix-36179
Draft

[WIP][Windows] Fix Entry.Completed firing during IME candidate confirmation#36577
HarishwaranVijayakumar wants to merge 3 commits into
dotnet:mainfrom
HarishwaranVijayakumar:fix-36179

Conversation

@HarishwaranVijayakumar

Copy link
Copy Markdown
Contributor

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!

Issue Details

  • On Windows, pressing Enter inside a Chinese/Japanese IME candidate window unexpectedly fires Entry.Completed, when the user only intended to confirm a word suggestion

Root Cause of the issue

  • When an IME candidate window is active and Enter is pressed to confirm a candidate, the IME completely intercepts and swallows the KeyDown event — the TextBox never receives a KeyDown event at all. However, when the user physically releases the Enter key, Windows still sends a KeyUp(Enter=13) to the TextBox because the key release cannot be suppressed.
  • The existing MAUI handler OnPlatformKeyUp fired Completed() whenever it saw VirtualKey.Enter in KeyUp — without ever checking whether a matching KeyDown(Enter) had actually occurred. Since the leaked KeyUp(Enter) from IME candidate confirmation looks identical to a real Enter's KeyUp , the handler had no way to tell them apart and fired Completed() every time

Description of Change

IME Enter key handling improvements:

  • Added an _enterKeyDownSeen flag and updated the key event logic in EntryHandler.Windows.cs to suppress the Completed event when Enter is used to confirm an IME candidate, ensuring it only fires on actual user submissions.

Testing:

  • Introduced the SimulateIMECandidateEnter method and a new test, CompletedDoesNotFireOnIMECandidateEnter, in EntryHandlerTests.Windows.cs to simulate the IME candidate confirmation scenario and verify that the Completed event is not triggered.

Issues Fixed

Fixes #36179

Tested the behaviour in the following platforms

  • Windows
  • Android
  • iOS
  • Mac
Before After
Beforefix-36179.mp4
Afterfix-36179.mp4

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36577

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36577"

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service dotnet-policy-service Bot added the community ✨ Community Contribution label Jul 15, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Hey there @@HarishwaranVijayakumar! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Jul 15, 2026
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 15, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 1 findings

See inline comments for details.

handler.PlatformView.Focus(Microsoft.UI.Xaml.FocusState.Programmatic);
await Task.Delay(100);

SimulateIMECandidateEnter(handler.PlatformView);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AI-Generated Review (multi-model)

[moderate] Regression Prevention and Test Coverage — This negative test can pass without exercising the Entry handler because the injected key is global and the test never verifies that the TextBox actually received keyboard input. If Focus(...) fails or input injection is not routed to this PlatformView, completedCount stays 0 for the wrong reason. Please assert the focus/precondition and add a positive control (for example, inject a normal Enter KeyDown+KeyUp and verify Completed fires) before relying on the KeyUp-only IME simulation.

@MauiBot MauiBot added s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Jul 15, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review Summary

@HarishwaranVijayakumar — new AI review results are available based on this last commit: c8fa57a. To request a fresh review after new comments or commits, comment /review rerun.

Gate Inconclusive Confidence Low Platform Windows


🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate Result: ⚠️ INCONCLUSIVE

Platform: WINDOWS · Base: main · Merge base: 0395a53b

Test Without Fix (expect FAIL) With Fix (expect PASS)
📱 EntryHandlerTests (CompletedDoesNotFireOnIMECandidateEnter) Category=Entry ⚠️ ENV ERROR ⚠️ ENV ERROR
🔴 Without fix — 📱 EntryHandlerTests (CompletedDoesNotFireOnIMECandidateEnter): ⚠️ ENV ERROR · 98s

No log file found

🟢 With fix — 📱 EntryHandlerTests (CompletedDoesNotFireOnIMECandidateEnter): ⚠️ ENV ERROR · 126s

No log file found

⚠️ Failure Details

  • ⚠️ EntryHandlerTests (CompletedDoesNotFireOnIMECandidateEnter) without fix: You cannot call a method on a null-valued expression.
  • ⚠️ EntryHandlerTests (CompletedDoesNotFireOnIMECandidateEnter) with fix: You cannot call a method on a null-valued expression.
📁 Fix files reverted (1 files)
  • src/Core/src/Handlers/Entry/EntryHandler.Windows.cs

📱 UI Tests — Entry,ViewBaseTests

Detected UI test categories: Entry,ViewBaseTests

Deep UI tests — 28 passed, 74 failed across 1 category on platform-pool agent (replaces in-process counts above). The HostApp failed to compile for 1 category (ViewBaseTests), so those deep UI tests could not run. A compile failure here is frequently a base-branch build break rather than this PR — check the build-output.log in the drop-deep-uitests artifact. Once the build is fixed, re-run the review to execute these tests.

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Entry 28/102 (74 ❌)
Entry — 74 failed tests
NestedEntryViewInFrameShouldNotCrash
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScriptCommand(String script, String commandName, Object[] args)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp
...
VerifyTextWhenFontAttributesSet
OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /elements' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
TearDown : OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.FindElements(By by)
   at UITest.Appium.AppiumQuery.FindElements(AppiumApp appiumApp) in /_/src/TestUtils/src/UITest.Appium/AppiumQuery.cs:line 125
   at UITest.Appium.AppiumQueryabl
...
VerifyPlaceholderWithPasswordTrue
OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /elements' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
TearDown : OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.FindElements(By by)
   at UITest.Appium.AppiumQuery.FindElements(AppiumApp appiumApp) in /_/src/TestUtils/src/UITest.Appium/AppiumQuery.cs:line 125
   at UITest.Appium.AppiumQueryabl
...
EntryAndEditorPlaceholderTextColorAppThemeBindingUpdatesOnThemeChange
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 145
   at
...
ClearingEntryWithStringFormatBindingShouldNotCrash
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 145
   at
...
Issue30071PlaceholderCharacterSpacingShouldChange
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 145
   at
...
UnfocusingPasswordDoesNotHang
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScriptCommand(String script, String commandName, Object[] args)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp
...
Bugzilla36559Test
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScriptCommand(String script, String commandName, Object[] args)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp
...
VerifyEntryHorizontalEndTextAlignmentPosition
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 145
   at
...
VerifyPlaceholderWithFontFamily
OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /elements' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
TearDown : OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.FindElements(By by)
   at UITest.Appium.AppiumQuery.FindElements(AppiumApp appiumApp) in /_/src/TestUtils/src/UITest.Appium/AppiumQuery.cs:line 125
   at UITest.Appium.AppiumQueryabl
...
VerifyTextWhenCursorPositionValueSet
OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /elements' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
TearDown : OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.FindElements(By by)
   at UITest.Appium.AppiumQuery.FindElements(AppiumApp appiumApp) in /_/src/TestUtils/src/UITest.Appium/AppiumQuery.cs:line 125
   at UITest.Appium.AppiumQueryabl
...
TapGestureRecognizerNotWorkingOnEntry
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScriptCommand(String script, String commandName, Object[] args)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp
...
VerifyTextWhenFontSizeSetCorrectly
OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /elements' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
TearDown : OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.FindElements(By by)
   at UITest.Appium.AppiumQuery.FindElements(AppiumApp appiumApp) in /_/src/TestUtils/src/UITest.Appium/AppiumQuery.cs:line 125
   at UITest.Appium.AppiumQueryabl
...
VerifyTextWhenClearButtonVisibleSetNever
OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /elements' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
TearDown : OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.FindElements(By by)
   at UITest.Appium.AppiumQuery.FindElements(AppiumApp appiumApp) in /_/src/TestUtils/src/UITest.Appium/AppiumQuery.cs:line 125
   at UITest.Appium.AppiumQueryabl
...
EntryCompletedShouldOnlyFireOnce
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 145
   at
...
VerifyPlaceholderWithClearButtonVisible
OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /elements' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
TearDown : OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.FindElements(By by)
   at UITest.Appium.AppiumQuery.FindElements(AppiumApp appiumApp) in /_/src/TestUtils/src/UITest.Appium/AppiumQuery.cs:line 125
   at UITest.Appium.AppiumQueryabl
...
TestFocusIsOnTheEndAfterSettingText
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 145
   at
...
EntryClearButtonShouldBeVisibleOnDarkTheme
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 145
   at
...
VerifyPlaceholderWithHorizontalAlignment
OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /elements' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
TearDown : OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.FindElements(By by)
   at UITest.Appium.AppiumQuery.FindElements(AppiumApp appiumApp) in /_/src/TestUtils/src/UITest.Appium/AppiumQuery.cs:line 125
   at UITest.Appium.AppiumQueryabl
...
EntryOneWayBindingShouldUpdate
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScriptCommand(String script, String commandName, Object[] args)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp
...
VerifyEntryClearButtonVisibilitySetToNever
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 145
   at
...
Issue3385Test
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 145
   at
...
VerifyPlaceholderWhenFlowDirectionSet
OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: Could not proxy command to the remote server. Original error: read ECONNRESET
TearDown : OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.FindElements(String mechanism, String value)
   at OpenQA.Selenium.Appium.AppiumDriver.FindElements(By by)
   at UITest.Appium.AppiumQuery.FindElement(AppiumApp appiumA
...
EntryClearButtonWorksEntryDoesntClearWhenNotClickingOnClear
The app was expected to be running still, investigate as possible crash
TearDown : The app was expected to be running still, investigate as possible crash
at UITest.Appium.NUnit.UITestBase.UITestBaseTearDown() in /_/src/TestUtils/src/UITest.NUnit/UITestBase.cs:line 159
   at UITest.Appium.NUnit.UITestBase.TestTearDown() in /_/src/TestUtils/src/UITest.NUnit/UITestBase.cs:line 45
   at InvokeStub_UITestBase.TestTearDown(Object, Object, IntPtr*)

--TearDown
   at UITest.Appium.NUnit.UITestBase.UITestBaseTearDown() in /_/src/TestUtils/src/UITest.NUnit/UITestBase.cs:line 159
   at UITest.Appium.NUnit.UITestBase.TestTearDown() in /_/src/TestUtils/src/UITest.NUnit/UITestBase.cs:line 45
   at InvokeStub_UITestBase.TestTearDown(Object, Object, IntPtr*)

1)    at UITest.Appium.NUnit.UITestBase.UITestBaseTearDown() in /_/src/TestUtils/src/UITest.NUnit/UITestBase.cs:line 159
   at UITest.Appium.NUnit.UITestBase.TestTearDown() in /_/src/TestUtils/src/UITest.NUnit/UITestBase.cs:line 45
  
...
EntrySelectAllOnRefocusReplacesText
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 145
   at
...
ControlCanBeFocusedByUnfocusedEvent
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScriptCommand(String script, String commandName, Object[] args)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp
...
EntryBoundToDouble_AllowsTypingSmallNegativeDecimal
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 145
   at
...
VerifyPlaceholderWithFontAttributes
OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /elements' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
TearDown : OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.FindElements(By by)
   at UITest.Appium.AppiumQuery.FindElements(AppiumApp appiumApp) in /_/src/TestUtils/src/UITest.Appium/AppiumQuery.cs:line 125
   at UITest.Appium.AppiumQueryabl
...
EntryMaxLengthEnforcedOnIOS26
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 145
   at
...
TestCursorColorOniOS
OneTimeSetUp: OpenQA.Selenium.UnknownErrorException : An unknown server-side error occurred while processing the command. Original error: 'POST /appium/app/launch' cannot be proxied to WinAppDriver server because its process is not running (probably crashed). Check the Appium log for more details
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
   at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.WebDriver.ExecuteScriptCommand(String script, String commandName, Object[] args)
   at OpenQA.Selenium.WebDriver.ExecuteScript(String script, Object[] args)
   at UITest.Appium.AppiumLifecycleActions.LaunchApp(IDictionary`2 parameters) in /_/src/TestUtils/src/UITest.Appium/Actions/AppiumLifecycleActions.cs:line 88
   at UITest.Appium.AppiumLifecycleActions.ResetApp
...

(+44 more — see TRX in artifact)

🔍 AI analysis of failures — PR-related vs unrelated

🔍 AI-generated triage (GitHub Copilot CLI) — a heuristic judgement of whether each deep UI test failure is connected to this PR's changes. Verify before relying on it.

Likely unrelated: the failures appear pre-existing, flaky, or infrastructure.

  • ● Unrelated — Windows Appium/WinAppDriver process failure across Entry tests (~73 tests): the dominant error is WinAppDriver not running or ECONNRESET during launch/element lookup/teardown, which is a session/driver infrastructure pattern rather than an Entry assertion failure or stack through the PR-modified handler code.
  • ● Unrelated — Possible app/driver crash while running an Entry clear-button test (~1 test): EntryClearButtonWorksEntryDoesntClearWhenNotClickingOnClear reports the app was no longer running, but the PR only changes Enter key completion handling and the surrounding run immediately collapses into WinAppDriver-not-running errors.

Strongest signal: despite the PR touching Windows Entry code, the failures are all driver/session crash symptoms with no failing assertion tied to the new Enter KeyDown/KeyUp logic.

📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)


📋 Pre-Flight — Context & Validation

Issue: #unknown - GitHub metadata unavailable (gh unauthenticated)
PR: #36577 - Local squashed review commit (PR #36577 squashed for review)
Platforms Affected: Windows
Files Changed: 1 implementation, 1 test

Key Findings

  • The PR changes Windows EntryHandler so Completed fires only when KeyUp(Enter) was preceded by KeyDown(Enter), intended to suppress IME candidate-confirmation Enter release events.
  • The added Windows device test injects KeyUp-only Enter with InputInjector, but InputInjector.TryCreate() is unchecked and can be unavailable in CI.
  • The handler tracks _enterKeyDownSeen per handler but does not clear it on disconnect, leaving a stale-state reconnect edge case.
  • GitHub PR body, linked issue, comments, reviews, and required checks could not be fetched because gh is not authenticated; analysis used local HEAD~1..HEAD diff.

Code Review Summary

Verdict: NEEDS_CHANGES
Confidence: low
Errors: 2 | Warnings: 1 | Suggestions: 0

Key code review findings:

  • src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Windows.cs:205-210InputInjector.TryCreate() may return null, so the test can crash before validating behavior.
  • src/Core/src/Handlers/Entry/EntryHandler.Windows.cs:45-55_enterKeyDownSeen is not reset during disconnect/reconnect.
  • src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Windows.cs:220-236 — regression coverage lacks a positive normal Enter submission path.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #36577 Track KeyDown(Enter) and require it before KeyUp(Enter) fires Completed; add KeyUp-only Windows test ⚠️ INCONCLUSIVE (Gate) EntryHandler.Windows.cs, EntryHandlerTests.Windows.cs Original PR; code review found stale-state and test robustness issues

🔬 Code Review — Deep Analysis

Code Review — PR #36577

Independent Assessment

What this changes: Windows EntryHandler now requires a preceding KeyDown(Enter) before KeyUp(Enter) fires Entry.Completed, suppressing IME candidate-confirmation KeyUp events. It adds a Windows device test that injects a KeyUp-only Enter sequence.

Inferred motivation: Prevent Windows IME composition confirmation from incorrectly triggering Completed.

Reconciliation with PR Narrative

Author claims: Blocked — gh is unauthenticated in this environment.
Agreement/disagreement: Code-only assessment supports the inferred intent, but PR metadata could not be verified.

Prior Review Reconciliation

Prior ❌ Error Finding Source Status Evidence
Blocked GitHub reviews/comments unavailable Unknown gh requires authentication

Blast Radius Assessment

  • Runs for all instances: Yes — all Windows Entry handlers subscribe to KeyDown/KeyUp.
  • Startup impact: No.
  • Static/shared state: No; state is per-handler.

CI Status

  • Required-check result: blocked
  • Classification: undetermined
  • Action taken: gh pr checks --required unavailable due missing auth; confidence capped low.

Findings

❌ Error — Test can crash because InputInjector.TryCreate() is unchecked

src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Windows.cs:205-210

InputInjector.TryCreate() may return null when input injection is unavailable or the required restricted capability is absent. The Core device-test manifest only declares runFullTrust, not inputInjectionBrokered, so injector.InjectKeyboardInput(...) can throw NullReferenceException instead of validating the fix.

❌ Error — _enterKeyDownSeen can survive handler disconnect/reconnect

src/Core/src/Handlers/Entry/EntryHandler.Windows.cs:45-55

DisconnectHandler unsubscribes events but does not reset _enterKeyDownSeen. If Enter KeyDown is observed and the handler disconnects before the matching KeyUp, the next reconnect can incorrectly treat a later IME-only KeyUp(Enter) as a real submission and fire Completed.

⚠️ Warning — Regression test only covers suppression path

src/Core/tests/DeviceTests/Handlers/Entry/EntryHandlerTests.Windows.cs:220-236

There is no Windows positive-path test proving normal KeyDown(Enter) + KeyUp(Enter) still fires Completed. Without that pair, the new test can pass even if normal Enter submission is broken.

Failure-Mode Probing

  • Handler disconnect after Enter down: stale _enterKeyDownSeen remains true and can cause a false Completed.
  • Input injection unavailable in CI: test dereferences a null injector and fails before exercising behavior.
  • Normal Enter path: not covered by the added test.

Verdict: NEEDS_CHANGES

Confidence: low
Summary: The approach is directionally sound, but the test has a likely null crash and the handler state should be reset on disconnect. CI and PR metadata were blocked by unavailable gh authentication.


🛠️ Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix-1 KeyDown-only completion; remove KeyUp handler and _enterKeyDownSeen ❌ Fail (runner blocked after build) 1 file Built, but changes Completed timing from KeyUp to KeyDown; no test pass
2 try-fix-2 Move key-pair tracking into MauiPasswordTextBox and expose internal EnterKeyCommitted ❌ Fail (runner blocked after build) 3 files Built, removes handler stale state, but broader/invasive and adds PublicAPI bookkeeping
3 try-fix-3 Use WinUI ProcessKeyboardAccelerators as Enter submit signal ❌ Fail (runner blocked after build) 1 file Built, distinct event source, but behavior unverified and accelerator semantics are uncertain
PR PR #36577 Gate KeyUp(Enter) with preceding KeyDown(Enter) flag in EntryHandler.Windows.cs ⚠️ INCONCLUSIVE (prior gate) 2 files Original PR; code review found stale-state and test robustness concerns

Cross-Pollination

Model/Source Round New Ideas? Details
claude-opus-4.6 expert 1 Yes KeyDown-only completion; suppress IME by ignoring KeyUp entirely
claude-opus-4.7 expert 1 Yes Same KeyDown-only family, emphasizing auto-repeat handling and InputInjector test risk
gpt-5.3-codex expert 1 Yes Platform-view semantic event in MauiPasswordTextBox
Orchestrator synthesis 2 Yes WinUI ProcessKeyboardAccelerators as a third event-source alternative

Exhausted: Yes — explored three meaningfully different roots: KeyDown timing, platform-view semantic event, and keyboard accelerator pipeline. Remaining variations would be minor changes to the PR's KeyDown/KeyUp flag (for example resetting the flag on disconnect), which are not meaningfully independent alternatives.

Selected Fix: None of the alternatives — no candidate passed all tests. The PR's fix remains unverified and has code-review concerns (_enterKeyDownSeen stale on disconnect/reconnect, unchecked InputInjector.TryCreate(), missing positive-path test).

Candidate Details

Try-Fix 1 — KeyDown-only completion

Approach

Move Windows Entry completion from KeyUp(Enter) to KeyDown(Enter) and remove the PR's _enterKeyDownSeen cross-event state. This suppresses IME candidate confirmation by ignoring KeyUp entirely.

Diff

``diff
diff --git a/src/Core/src/Handlers/Entry/EntryHandler.Windows.cs b/src/Core/src/Handlers/Entry/EntryHandler.Windows.cs
index b239ff1867..fe8a884f87 100644
--- a/src/Core/src/Handlers/Entry/EntryHandler.Windows.cs
+++ b/src/Core/src/Handlers/Entry/EntryHandler.Windows.cs
@@ -14,10 +14,6 @@ namespace Microsoft.Maui.Handlers
static readonly bool s_shouldBeDelayed = DeviceInfo.Idiom != DeviceIdiom.Desktop;
bool _set;

  •   // Some Windows IMEs use Enter to confirm the current composition.
    
  •   // Those Enter presses should not trigger Entry.Completed.
    
  •   bool _enterKeyDownSeen;
    
  •   protected override TextBox CreatePlatformView() =>
      	new MauiPasswordTextBox()
      	{
    

@@ -37,7 +33,6 @@ namespace Microsoft.Maui.Handlers
protected override void ConnectHandler(TextBox platformView)
{
platformView.KeyDown += OnPlatformKeyDown;

  •   	platformView.KeyUp += OnPlatformKeyUp;
      	platformView.TextChanged += OnPlatformTextChanged;
      	platformView.SizeChanged += OnPlatformViewSizeChanged;
      }
    

@@ -46,7 +41,6 @@ namespace Microsoft.Maui.Handlers
{
platformView.SizeChanged -= OnPlatformViewSizeChanged;
platformView.KeyDown -= OnPlatformKeyDown;

  •   	platformView.KeyUp -= OnPlatformKeyUp;
      	platformView.TextChanged -= OnPlatformTextChanged;
    
      	if (_set)
    

@@ -121,31 +115,17 @@ namespace Microsoft.Maui.Handlers
}

	void OnPlatformKeyDown(object? sender, KeyRoutedEventArgs args)
  •   {
    
  •   	if (args?.Key == VirtualKey.Enter)
    
  •   	{
    
  •   		_enterKeyDownSeen = true;
    
  •   	}
    
  •   }
    
  •   void OnPlatformKeyUp(object? sender, KeyRoutedEventArgs args)
      {
      	if (args?.Key != VirtualKey.Enter)
      	{
      		return;
      	}
    
  •   	// Some Windows IMEs raise KeyDown(Process) followed by KeyUp(Enter)
    
  •   	// when Enter is used to confirm an IME candidate. Since this is not a
    
  •   	// user submission, suppress Completed unless an actual Enter KeyDown
    
  •   	// preceded the KeyUp.
    
  •   	if (!_enterKeyDownSeen)
    
  •   	if (args.KeyStatus.WasKeyDown)
      	{
      		return;
      	}
    
  •   	_enterKeyDownSeen = false;
    
  •   	if (VirtualView?.ReturnType == ReturnType.Next)
      	{
      		PlatformView?.TryMoveFocus(FocusNavigationDirection.Next);
    

``

Test Results

  • Command: pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Core -Platform windows -TestFilter "FullyQualifiedName~EntryHandlerTests"
  • Result: ❌ Fail / blocked by runner after successful build
  • Evidence: Get-WindowsDeviceTestResultSummary ... You cannot call a method on a null-valued expression.

Failure Analysis

The candidate built, but the test runner failed while parsing/summarizing Windows device-test output. This is not a verified pass. The approach also changes Completed timing from key release to key press, which may be a compatibility concern even if it avoids stale handler state.


Try-Fix 2 — Platform-view semantic Enter event

Approach

Move Enter key-pair discrimination into MauiPasswordTextBox and expose an internal EnterKeyCommitted event. EntryHandler subscribes to that semantic event and no longer owns _enterKeyDownSeen.

Diff

``diff
diff --git a/src/Core/src/Handlers/Entry/EntryHandler.Windows.cs b/src/Core/src/Handlers/Entry/EntryHandler.Windows.cs
index b239ff1867..a82283dec3 100644
--- a/src/Core/src/Handlers/Entry/EntryHandler.Windows.cs
+++ b/src/Core/src/Handlers/Entry/EntryHandler.Windows.cs
@@ -14,10 +14,6 @@ namespace Microsoft.Maui.Handlers
static readonly bool s_shouldBeDelayed = DeviceInfo.Idiom != DeviceIdiom.Desktop;
bool _set;

  •   // Some Windows IMEs use Enter to confirm the current composition.
    
  •   // Those Enter presses should not trigger Entry.Completed.
    
  •   bool _enterKeyDownSeen;
    
  •   protected override TextBox CreatePlatformView() =>
      	new MauiPasswordTextBox()
      	{
    

@@ -36,8 +32,9 @@ namespace Microsoft.Maui.Handlers

	protected override void ConnectHandler(TextBox platformView)
	{
  •   	platformView.KeyDown += OnPlatformKeyDown;
    
  •   	platformView.KeyUp += OnPlatformKeyUp;
    
  •   	if (platformView is MauiPasswordTextBox mauiTextBox)
    
  •   		mauiTextBox.EnterKeyCommitted += OnPlatformEnterKeyCommitted;
    
  •   	platformView.TextChanged += OnPlatformTextChanged;
      	platformView.SizeChanged += OnPlatformViewSizeChanged;
      }
    

@@ -45,8 +42,10 @@ namespace Microsoft.Maui.Handlers
protected override void DisconnectHandler(TextBox platformView)
{
platformView.SizeChanged -= OnPlatformViewSizeChanged;

  •   	platformView.KeyDown -= OnPlatformKeyDown;
    
  •   	platformView.KeyUp -= OnPlatformKeyUp;
    
  •   	if (platformView is MauiPasswordTextBox mauiTextBox)
    
  •   		mauiTextBox.EnterKeyCommitted -= OnPlatformEnterKeyCommitted;
    
  •   	platformView.TextChanged -= OnPlatformTextChanged;
    
      	if (_set)
    

@@ -120,32 +119,8 @@ namespace Microsoft.Maui.Handlers
VirtualView?.UpdateText(PlatformView.Text);
}

  •   void OnPlatformKeyDown(object? sender, KeyRoutedEventArgs args)
    
  •   {
    
  •   	if (args?.Key == VirtualKey.Enter)
    
  •   	{
    
  •   		_enterKeyDownSeen = true;
    
  •   	}
    
  •   }
    
  •   void OnPlatformKeyUp(object? sender, KeyRoutedEventArgs args)
    
  •   void OnPlatformEnterKeyCommitted(object? sender, EventArgs args)
      {
    
  •   	if (args?.Key != VirtualKey.Enter)
    
  •   	{
    
  •   		return;
    
  •   	}
    
  •   	// Some Windows IMEs raise KeyDown(Process) followed by KeyUp(Enter)
    
  •   	// when Enter is used to confirm an IME candidate. Since this is not a
    
  •   	// user submission, suppress Completed unless an actual Enter KeyDown
    
  •   	// preceded the KeyUp.
    
  •   	if (!_enterKeyDownSeen)
    
  •   	{
    
  •   		return;
    
  •   	}
    
  •   	_enterKeyDownSeen = false;
    
  •   	if (VirtualView?.ReturnType == ReturnType.Next)
      	{
      		PlatformView?.TryMoveFocus(FocusNavigationDirection.Next);
    

diff --git a/src/Core/src/Platform/Windows/MauiPasswordTextBox.cs b/src/Core/src/Platform/Windows/MauiPasswordTextBox.cs
index fb2aaa9f65..33eb601fe5 100644
--- a/src/Core/src/Platform/Windows/MauiPasswordTextBox.cs
+++ b/src/Core/src/Platform/Windows/MauiPasswordTextBox.cs
@@ -56,8 +56,11 @@ namespace Microsoft.Maui.Platform
bool _internalChangeFlag;
int _cachedCursorPosition;
int _cachedTextLength;

  •   bool _enterKeyDownReceived;
      readonly long _token;
    
  •   internal event EventHandler? EnterKeyCommitted;
    
  •   public MauiPasswordTextBox()
      {
      	TextChanging += OnNativeTextChanging;
    

@@ -109,6 +112,9 @@ namespace Microsoft.Maui.Platform
// handled accordingly.
protected override void OnKeyDown(KeyRoutedEventArgs e)
{

  •   	if (e.Key == VirtualKey.Enter)
    
  •   		_enterKeyDownReceived = true;
    
  •   	if (!IsPassword)
      	{
      		base.OnKeyDown(e);
    

@@ -152,6 +158,20 @@ namespace Microsoft.Maui.Platform
base.OnKeyDown(e);
}

  •   protected override void OnKeyUp(KeyRoutedEventArgs e)
    
  •   {
    
  •   	base.OnKeyUp(e);
    
  •   	if (e.Key != VirtualKey.Enter)
    
  •   		return;
    
  •   	if (!_enterKeyDownReceived)
    
  •   		return;
    
  •   	_enterKeyDownReceived = false;
    
  •   	EnterKeyCommitted?.Invoke(this, EventArgs.Empty);
    
  •   }
    
  •   protected override AutomationPeer OnCreateAutomationPeer()
      {
      	return new MauiPasswordTextBoxAutomationPeer(this);
    

diff --git a/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt b/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
index 4151228a79..866dbb8fcd 100644
--- a/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
+++ b/src/Core/src/PublicAPI/net-windows/PublicAPI.Unshipped.txt
@@ -1,5 +1,6 @@
#nullable enable
override Microsoft.Maui.Platform.LayoutPanel.OnCreateAutomationPeer() -> Microsoft.UI.Xaml.Automation.Peers.AutomationPeer!
override Microsoft.Maui.Platform.MauiPasswordTextBox.OnCreateAutomationPeer() -> Microsoft.UI.Xaml.Automation.Peers.AutomationPeer!
+override Microsoft.Maui.Platform.MauiPasswordTextBox.OnKeyUp(Microsoft.UI.Xaml.Input.KeyRoutedEventArgs! e) -> void
override Microsoft.Maui.Platform.ContentPanel.MeasureOverride(Windows.Foundation.Size availableSize) -> Windows.Foundation.Size
override Microsoft.Maui.Platform.ContentPanel.OnCreateAutomationPeer() -> Microsoft.UI.Xaml.Automation.Peers.AutomationPeer!

``

Test Results

  • Command: pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Core -Platform windows -TestFilter "FullyQualifiedName~EntryHandlerTests"
  • Build: ✅ Succeeded (� Error(s))
  • Result: ❌ Fail / blocked by runner result summarization
  • Evidence: Get-WindowsDeviceTestResultSummary ... You cannot call a method on a null-valued expression.

Failure Analysis

The candidate is unverified because the Windows runner failed after build. Compared with the PR, this approach removes handler stale state but is more invasive: it adds keyboard semantics and a protected override to MauiPasswordTextBox, requiring PublicAPI bookkeeping.


Try-Fix 3 — ProcessKeyboardAccelerators submission

Approach

Subscribe to WinUI ProcessKeyboardAccelerators and treat Enter accelerator processing as the semantic user-submit signal. This avoids both KeyUp gating and platform-view OnKeyUp overrides.

Diff

``diff
diff --git a/src/Core/src/Handlers/Entry/EntryHandler.Windows.cs b/src/Core/src/Handlers/Entry/EntryHandler.Windows.cs
index b239ff1867..9a78b2ac48 100644
--- a/src/Core/src/Handlers/Entry/EntryHandler.Windows.cs
+++ b/src/Core/src/Handlers/Entry/EntryHandler.Windows.cs
@@ -14,10 +14,6 @@ namespace Microsoft.Maui.Handlers
static readonly bool s_shouldBeDelayed = DeviceInfo.Idiom != DeviceIdiom.Desktop;
bool _set;

  •   // Some Windows IMEs use Enter to confirm the current composition.
    
  •   // Those Enter presses should not trigger Entry.Completed.
    
  •   bool _enterKeyDownSeen;
    
  •   protected override TextBox CreatePlatformView() =>
      	new MauiPasswordTextBox()
      	{
    

@@ -36,8 +32,7 @@ namespace Microsoft.Maui.Handlers

	protected override void ConnectHandler(TextBox platformView)
	{
  •   	platformView.KeyDown += OnPlatformKeyDown;
    
  •   	platformView.KeyUp += OnPlatformKeyUp;
    
  •   	platformView.ProcessKeyboardAccelerators += OnPlatformProcessKeyboardAccelerators;
      	platformView.TextChanged += OnPlatformTextChanged;
      	platformView.SizeChanged += OnPlatformViewSizeChanged;
      }
    

@@ -45,8 +40,7 @@ namespace Microsoft.Maui.Handlers
protected override void DisconnectHandler(TextBox platformView)
{
platformView.SizeChanged -= OnPlatformViewSizeChanged;

  •   	platformView.KeyDown -= OnPlatformKeyDown;
    
  •   	platformView.KeyUp -= OnPlatformKeyUp;
    
  •   	platformView.ProcessKeyboardAccelerators -= OnPlatformProcessKeyboardAccelerators;
      	platformView.TextChanged -= OnPlatformTextChanged;
    
      	if (_set)
    

@@ -120,38 +114,20 @@ namespace Microsoft.Maui.Handlers
VirtualView?.UpdateText(PlatformView.Text);
}

  •   void OnPlatformKeyDown(object? sender, KeyRoutedEventArgs args)
    
  •   {
    
  •   	if (args?.Key == VirtualKey.Enter)
    
  •   	{
    
  •   		_enterKeyDownSeen = true;
    
  •   	}
    
  •   }
    
  •   void OnPlatformKeyUp(object? sender, KeyRoutedEventArgs args)
    
  •   void OnPlatformProcessKeyboardAccelerators(UIElement sender, ProcessKeyboardAcceleratorEventArgs args)
      {
    
  •   	if (args?.Key != VirtualKey.Enter)
    
  •   	if (args.Key != VirtualKey.Enter)
      	{
      		return;
      	}
    
  •   	// Some Windows IMEs raise KeyDown(Process) followed by KeyUp(Enter)
    
  •   	// when Enter is used to confirm an IME candidate. Since this is not a
    
  •   	// user submission, suppress Completed unless an actual Enter KeyDown
    
  •   	// preceded the KeyUp.
    
  •   	if (!_enterKeyDownSeen)
    
  •   	{
    
  •   		return;
    
  •   	}
    
  •   	_enterKeyDownSeen = false;
    
  •   	if (VirtualView?.ReturnType == ReturnType.Next)
      	{
      		PlatformView?.TryMoveFocus(FocusNavigationDirection.Next);
      	}
    
      	VirtualView?.Completed();
    
  •   	args.Handled = true;
      }
    
      void OnPlatformSelectionChanged(object sender, RoutedEventArgs e)
    

``

Test Results

  • Command: pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Core -Platform windows -TestFilter "FullyQualifiedName~EntryHandlerTests"
  • Build: ✅ Succeeded (� Error(s))
  • Result: ❌ Fail / blocked by runner result summarization
  • Evidence: Get-WindowsDeviceTestResultSummary ... You cannot call a method on a null-valued expression.

Failure Analysis

The candidate is unverified because the Windows runner failed after build. It is meaningfully different from the PR and prior candidates, but keyboard accelerator handling may not perfectly match TextBox Enter semantics under IME/focus conditions.


📝 Recommended PR Title & Description

Assessment: ✏️ Recommend updating — the current description is strong, but the title has a [WIP] noise prefix and the winning pr-plus-reviewer fix adds lifecycle cleanup and stronger regression-test preconditions not reflected in the current metadata.

Recommended title

[Windows] Entry: Suppress Completed during IME candidate confirmation

Recommended description

### Issue Details

- On Windows, pressing Enter inside a Chinese/Japanese IME candidate window unexpectedly fires Entry.Completed, when the user only intended to confirm a word suggestion.

### Root Cause of the issue

- When an IME candidate window is active and Enter is pressed to confirm a candidate, the IME completely intercepts and swallows the KeyDown event — the TextBox never receives a KeyDown event at all. However, when the user physically releases the Enter key, Windows still sends a KeyUp(Enter=13) to the TextBox because the key release cannot be suppressed.
- The existing MAUI handler OnPlatformKeyUp fired Completed() whenever it saw VirtualKey.Enter in KeyUp — without ever checking whether a matching KeyDown(Enter) had actually occurred. Since the leaked KeyUp(Enter) from IME candidate confirmation looks identical to a real Enter KeyUp, the handler had no way to tell them apart and fired Completed() every time.

### Description of Change

**IME Enter key handling improvements:**

- Added an `_enterKeyDownSeen` flag in `EntryHandler.Windows.cs` so `Entry.Completed` only fires when a `KeyUp(Enter)` was preceded by a real `KeyDown(Enter)` received by the TextBox.
- Clears `_enterKeyDownSeen` during handler disconnect to avoid carrying stale key state across handler lifecycle transitions.
- Preserves normal Enter submission behavior, including `ReturnType.Next` focus movement, while suppressing IME candidate-confirmation KeyUp-only sequences.

**Testing:**

- Introduced a Windows regression test, `CompletedDoesNotFireOnIMECandidateEnter`, in `EntryHandlerTests.Windows.cs`.
- Added `InputInjector` helpers to simulate both a normal Enter KeyDown+KeyUp sequence and the IME candidate-confirmation KeyUp-only sequence.
- The test asserts the TextBox focus/input preconditions, verifies a normal Enter fires `Completed`, then verifies the IME KeyUp-only sequence does not fire `Completed` again.

### Issues Fixed

Fixes #36179

### Tested the behaviour in the following platforms

- [x] Windows
- [ ] Android
- [ ] iOS
- [ ] Mac

| Before | After |
|----------|----------|
| <video src="https://github.com/user-attachments/assets/75b49092-3a3e-48ea-bdc7-6739c3230ab1"> | <video src="https://github.com/user-attachments/assets/68f72736-227c-4393-bc6c-8196c5371cb6"> |

🏁 Report — Final Recommendation

Comparative Analysis — PR #36577

Candidate Ranking

Rank Candidate Regression result Assessment
1 pr-plus-reviewer ⚠️ Inconclusive / not rerun Best candidate. Keeps the PR's localized KeyDown/KeyUp semantics, fixes stale state on disconnect, and strengthens the test so the negative IME simulation cannot pass merely because input was not routed.
2 pr ⚠️ Inconclusive gate Core approach is appropriate and lowest-risk, but raw PR has test robustness issues and leaves _enterKeyDownSeen stale across disconnect/reconnect.
3 try-fix-2 ❌ Failed/blocked runner after build Removes handler stale state by moving semantics into MauiPasswordTextBox, but is broader and more invasive, adds a protected override/PublicAPI bookkeeping, and remains unverified.
4 try-fix-1 ❌ Failed/blocked runner after build Simpler state-free approach, but changes Completed timing from KeyUp to KeyDown, which is a compatibility risk for existing Windows Entry behavior.
5 try-fix-3 ❌ Failed/blocked runner after build Meaningfully distinct, but ProcessKeyboardAccelerators may not preserve TextBox Enter semantics under IME/focus conditions and the approach is unverified.

Regression-Test Rule Applied

No candidate has a verified passing regression result. The try-fix-* candidates are recorded as ❌ failed/blocked after build in STEP 5a and therefore are ranked below the PR-family candidates, whose gate status is explicitly inconclusive and must not be treated as a failing fix.

Why pr-plus-reviewer Wins

pr-plus-reviewer preserves the raw PR's narrowly targeted behavior: Completed still fires on normal Enter KeyDown+KeyUp and is suppressed for IME KeyUp-only candidate confirmation. It improves the raw PR without adopting the larger risks introduced by the independent try-fix candidates: no completion-timing shift, no new platform-view event/API surface, and no uncertain keyboard-accelerator semantics.

The winning changes are also directly responsive to review feedback. The sandbox candidate clears lifecycle state during disconnect and upgrades the regression test from a pure negative assertion to a positive-control-plus-negative sequence, making the test meaningfully prove that input reached the focused TextBox.

Winning Candidate

Winner: pr-plus-reviewer

Rationale: Same localized fix as the PR, plus reviewer-driven hardening for handler lifecycle state and regression-test reliability. The raw PR is acceptable in concept, but pr-plus-reviewer is the best merge target among available candidates.


🧭 Next Steps — review latest findings

No alternative fix was selected for this run. Review the session findings and CI results before merging.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Entry.Completed accidentally fired on Enter key press in the 'Conversion candidate window' in Chinese/Japanese IME

4 participants