dotnet/wpf · error · InvalidOperationException
SR.KeyboardSinkNotAChild
Error message
SR.KeyboardSinkNotAChild
What it means
When an HwndSource child keyboard-input site is being detached, the code removes it from _keyboardInputSinkChildren; if the site is not in that list, Remove returns false and the code throws InvalidOperationException(SR.KeyboardSinkNotAChild). This means the caller tried to unregister a keyboard sink that this HwndSource never (or no longer) owns.
Solutions
- Unregister each child sink exactly once — guard removal with a flag so disposal paths don't run twice.
- Only remove a site from the same HwndSource instance that registered it; verify parent identity before removal.
- Check that the removal isn't triggered after the source was disposed, which may have already cleared the child list.
Example fix
// before
site.Dispose();
site.Dispose(); // second call throws KeyboardSinkNotAChild
// after
private bool _siteRemoved;
public void RemoveSite(HwndSourceKeyboardInputSite site)
{
if (_siteRemoved) return;
_siteRemoved = true;
site.Dispose();
}
Defensive patterns
Strategy: validation
Validate before calling
if (site == null || _removedSites.Contains(site)) return; _removedSites.Add(site); site.Dispose();
Try / catch
try
{
site.Dispose();
}
catch (InvalidOperationException)
{
// site was not a child of this source (already removed or wrong parent)
} Prevention
- Make dispose/idempotent: guard removal with a boolean flag.
- Only remove sinks from the HwndSource that registered them.
- Avoid double teardown (handle-destroyed event + explicit dispose both removing).
When it happens
Trigger: Calling the child-site removal path with a HwndSourceKeyboardInputSite created by a different HwndSource, or a site that was already removed/disposed; double-disposal that unregisters the same site twice.
Common situations: Hosted interop control torn down twice (handle destroyed event plus explicit dispose); mismatched parent/child windows during reparenting; finalization racing with explicit cleanup.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- SR.D3DImage_MustHaveBackBuffer
- SR.Image_EndInitWithoutBeginInit
- SR.Image_MustBeLocked
- SR.Image_OnlyOneInit
- SR.KeyboardSinkAlreadyOwned
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/d1adaa6c02a2843f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSource.cs:2238
{
return _acquireHwndFocusInMenuMode;
}
}
/// <summary>
/// The method is not part of the interface (IKeyboardInputSink).
/// </summary>
/// <param name="site">The Site that containes the sink to unregister</param>
internal void CriticalUnregisterKeyboardInputSink(HwndSourceKeyboardInputSite site)
{
if(_isDisposed)
return;
if (null != _keyboardInputSinkChildren)
{
if (!_keyboardInputSinkChildren.Remove(site))
{
throw new InvalidOperationException(SR.KeyboardSinkNotAChild);
}
}
}
private IKeyboardInputSink ChildSinkWithFocus
{
get
{
IKeyboardInputSink ikis=null;
if(null == _keyboardInputSinkChildren)
return null;
foreach (HwndSourceKeyboardInputSite site in _keyboardInputSinkChildren)
{
IKeyboardInputSite isite = (IKeyboardInputSite)site;
if (isite.Sink.HasFocusWithin())View on GitHub (pinned to 81131a70a4)