dotnet/wpf · error · NotSupportedException
SR.WriteNotSupported
Error message
SR.WriteNotSupported
What it means
NotSupportedException (SR.WriteNotSupported) thrown by VersionedStreamOwner.PersistVersion when it needs to write/normalize the FormatVersion header but the underlying BaseStream is not writable (CanWrite == false). PersistVersion is invoked from WriteAttempt, so a write on a read-only versioned stream fails here first.
Solutions
- Open the backing stream with FileAccess.ReadWrite (or FileMode.OpenOrCreate) when updates are intended
- Check BaseStream.CanWrite before attempting writes and surface a clear error or copy the file to a writable location first
- Copy the read-only file to a temp location, update it there, and swap back on success
- Remove the read-only attribute / fix permissions if the file is meant to be writable
Example fix
// before: read-only stream then write
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
var vs = new VersionedStreamOwner(fs, ...);
vs.Write(data, 0, data.Length); // NotSupportedException
}
// after: open for update when writes are needed
using (var fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite))
{
var vs = new VersionedStreamOwner(fs, ...);
vs.Write(data, 0, data.Length); // OK
} Defensive patterns
Strategy: validation
Validate before calling
static void EnsureWritable(FileStream fs)
{
if (!fs.CanWrite)
throw new InvalidOperationException("Stream was opened read-only; reopen with FileAccess.ReadWrite to update.");
} Type guard
bool CanUpdate(VersionedStreamOwner owner) => owner?.BaseStream?.CanWrite == true;
Try / catch
try
{
versionedStreamOwner.Write(data, 0, data.Length);
}
catch (NotSupportedException)
{
// reopen the backing file with write access (or copy to temp) and retry
using (var rw = new FileStream(path, FileMode.Open, FileAccess.ReadWrite))
{
RetryWrite(rw, data);
}
} Prevention
- Open with FileAccess.ReadWrite whenever updates are planned
- Check File.Attributes for ReadOnly and clear it before opening for update
- Never assume a stream is writable because the file is — verify CanWrite first
- On read-only media, copy to a writable temp location before updating
When it happens
Trigger: Opening the compound file (or backing stream) with FileAccess.Read / read-only FileStream and then calling Write, WriteByte, or SetLength on the versioned stream — the first write triggers PersistVersion, which cannot write the version header.
Common situations: Files opened from read-only media or with read-only file attributes; streams opened FileAccess.Read out of caution then reused for updates; files under source control or opened from a zip/package exposed read-only.
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
- Image_OriginalStreamReadOnly
- SR.SetLengthNotSupported
- Stream does not support Write
- 0x80040209
- ArgumentOutOfRangeException(nameof(offset))
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/218cec3f5ccf8b27.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/CompoundFile/VersionedStreamOwner.cs:340
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
/// <summary>
/// Ensure that the version is persisted
/// </summary>
/// <remarks>Leaves stream at position just after the FormatVersion.
/// Destructive. This is called automatically from WriteAttempt but callers
/// can call directly if they have changed the stream contents to a format
/// that is no longer compatible with the persisted FormatVersion. If
/// this is not called directly, and a FormatVersion was found in the file
/// then only the Updater field is modified.
/// </remarks>
private void PersistVersion(FormatVersion version)
{
if (!BaseStream.CanWrite)
throw new NotSupportedException(SR.WriteNotSupported);
// normalize and save
long tempPos = checked(BaseStream.Position - _dataOffset);
BaseStream.Seek(0, SeekOrigin.Begin);
// update _dataOffset
long offset = version.SaveToStream(BaseStream);
_fileVersion = version; // we know what it is - no need to deserialize
// existing value - ensure we didn't change sizes as this could lead to
// data corruption
if ((_dataOffset != 0) && (offset != _dataOffset))
throw new FileFormatException(SR.VersionUpdateFailure);
// at this point we know the offset
_dataOffset = offset;
// restore and shiftView on GitHub (pinned to 81131a70a4)