dotnet/wpf · error · COMException
0x80040209
0x80040209
Error message
SR.TextStore_TS_E_READONLY
What it means
TextStore.SetText (ITextStoreACP::SetText implementation) throws this COMException with HRESULT TS_E_READONLY (0x80040209) when the underlying text container is read-only. TSF cannot modify text in a read-only store, so the modification request is rejected with the standard TSF read-only error code.
Solutions
- Make the target control editable (set IsReadOnly=false) before text services attempt composition.
- In the text service, check the TS_LF_READ-only status / document flags before calling SetText and fall back to not modifying text.
- Catch COMException with HRESULT TS_E_READONLY and treat the edit as rejected rather than crashing.
Example fix
// before
store.SetText(flags, start, end, chars, chars.Length, out change);
// after
if (!store.IsReadOnly)
store.SetText(flags, start, end, chars, chars.Length, out change); Defensive patterns
Strategy: validation
Validate before calling
if (!control.IsReadOnly) { store.SetText(flags, start, end, text, text.Length, out change); } Try / catch
try { store.SetText(...); } catch (COMException ex) when (ex.ErrorCode == unchecked((int)0x80040209)) { /* treat edit as rejected */ } Prevention
- Check IsReadOnly before any TSF write operation
- Avoid switching IsReadOnly while IME composition is active
- Handle TS_E_READONLY HRESULT explicitly in text services
When it happens
Trigger: An IME/text service calls SetText while the WPF control (e.g. read-only TextBox/RichTextBox) reports IsReadOnly == true.
Common situations: User focuses an IME composition into a read-only text box; automation or test harness writing text into a read-only document; app sets TextBox.IsReadOnly while composition is active.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/fe8a1d1bfc76bf04.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/TextStore.cs:474
}
nextIndex = navigator.CharOffset;
if (IsTracing)
{
IMECompositionTracer.Trace(this, IMECompositionTraceOp.GetText,
"text:", startIndex, endIndex, cchReq, charsCopied, new String(text, 0, charsCopied),
"runs:", cRunInfoReq, cRunInfoRcv,
"next:", nextIndex);
}
}
// See msdn's ITextStoreACP documentation for a full description.
public void SetText(UnsafeNativeMethods.SetTextFlags flags, int startIndex, int endIndex, char[] text, int cch, out UnsafeNativeMethods.TS_TEXTCHANGE change)
{
if (this.IsReadOnly)
{
throw new COMException(SR.TextStore_TS_E_READONLY, UnsafeNativeMethods.TS_E_READONLY);
}
if (IsTracing)
{
IMECompositionTracer.Trace(this, IMECompositionTraceOp.BSetText,
"text:", startIndex, endIndex, cch, new String(text, 0, cch));
}
ITextPointer start;
ITextPointer end;
GetNormalizedRange(startIndex, endIndex, out start, out end);
while (start != null && TextPointerBase.IsBeforeFirstTable(start))
{
start = start.GetNextInsertionPosition(LogicalDirection.Forward);
}
View on GitHub (pinned to 81131a70a4)