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

  1. Open the backing stream with FileAccess.ReadWrite (or FileMode.OpenOrCreate) when updates are intended
  2. Check BaseStream.CanWrite before attempting writes and surface a clear error or copy the file to a writable location first
  3. Copy the read-only file to a temp location, update it there, and swap back on success
  4. 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

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


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 shift

View on GitHub (pinned to 81131a70a4)