dotnet/wpf · error · COMException
STG_E_CANTSAVE
STG_E_CANTSAVE
Error message
The only IPersistFile operation supported by the component is Load.
What it means
IPersistFile.Save on XpsFilter is deliberately unimplemented: a filter is read-only, so the explicit interface method immediately throws COMException with STG_E_CANTSAVE and the message SR.FilterIPersistFileIsReadOnly. There is no code path that saves; the operation is unsupported by design.
Solutions
- Do not call Save on the filter; treat it as load-only
- Gate Save calls behind a capability check / feature flag for writable persistence
- Use the actual storage (XPS writer APIs) to produce files instead of the filter
Example fix
// before
((IPersistFile)filter).Save(path, true);
// after
// XpsFilter is read-only; write XPS via XpsDocument instead
using (var doc = new XpsDocument(path, FileAccess.Write)) { /* write */ } Defensive patterns
Strategy: try-catch
Try / catch
try { ((IPersistFile)filter).Save(path, true); }
catch (COMException ex) when (ex.HResult == NativeMethods.STG_E_CANTSAVE)
{ /* read-only component: use writer APIs instead */ } Prevention
- Treat filters as load-only; never wire them into save pipelines
- Check component capabilities before generic IPersistFile save calls
- Write XPS via XpsDocument, not the filter
When it happens
Trigger: Calling IPersistFile.Save (or SaveCompleted patterns that assume writability) on an XpsFilter instance obtained as IPersistFile.
Common situations: Generic persistence plumbing that round-trips every IPersistFile component; test harnesses exercising all interface members.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/d2e353f228f7f0c8.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/IO/Packaging/XpsFilter.cs:459
/// <summary>
/// Saves a copy of the object into the specified file.
/// </summary>
/// <param name="pszFileName">
/// A zero-terminated string containing the absolute path
/// of the file to which the object is saved.
/// </param>
/// <param name="fRemember">
/// Indicates whether pszFileName is to be used as the current working file.
/// </param>
/// <remarks>
/// On the odd chance that this link is still valid when it's needed,
/// expected error codes are described at
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/html/da9581e8-98c7-4592-8ee1-a1bc8232635b.asp
/// </remarks>
void IPersistFile.Save(string pszFileName, bool fRemember)
{
throw new COMException(SR.FilterIPersistFileIsReadOnly, NativeMethods.STG_E_CANTSAVE);
}
/// <summary>
/// Notifies the object that it can write to its file.
/// </summary>
/// <param name="pszFileName">
/// The absolute path of the file where the object was previously saved.
/// </param>
/// <remarks>
/// On the odd chance that this link is still valid when it's needed,
/// expected error codes are described at
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/html/da9581e8-98c7-4592-8ee1-a1bc8232635b.asp
/// This function should always return S_OK when Save is not supported.
/// </remarks>
void IPersistFile.SaveCompleted(string pszFileName)
{
return; // return S_OK
}View on GitHub (pinned to 81131a70a4)