Summary
Set-Content / Add-Content intermittently fail with:
Set-Content : Stream was not readable.
+ CategoryInfo : InvalidArgument: (<path>:String) [Set-Content], ArgumentException
+ FullyQualifiedErrorId : GetContentWriterArgumentError,Microsoft.PowerShell.Commands.SetContentCommand
This is a long-standing intermittent failure (reported by users for years, e.g. https://stackoverflow.com/q/48953767, https://stackoverflow.com/q/27370389), typically when the target file is momentarily contended by another process (a concurrent writer, an antivirus scan, a file watcher, OneDrive sync).
Repro / trigger
Hard to reproduce deterministically, but a reliable trigger is having two processes write the same (usually newly created) file with Set-Content concurrently, or writing a file that a file watcher / AV just opened. The error is non-deterministic but recurs under load.
Root cause
In src/System.Management.Automation/namespaces/FileSystemContentStream.cs, FileSystemContentReaderWriter.CreateStreams:
- When writing, it upgrades the requested access to
FileAccess.ReadWrite so it can detect the encoding (if ((fileAccess & FileAccess.Write) != 0) fileAccess = FileAccess.ReadWrite;, ~line 838).
- The first
new FileStream(path, fileMode, fileAccess, fileShare) (~line 853) throws IOException (e.g. sharing violation because another process holds the file), which is caught (~line 856) and retried with the original write-only requestedAccess (~line 866). That open succeeds.
- But the retry does not update the
fileAccess variable. The reader construction below still checks the upgraded value: if ((fileAccess & (FileAccess.Read)) != 0) { _reader = new StreamReader(_stream, fileEncoding); ... } (~line 873). Since fileAccess still contains the Read bit while _stream is now a write-only FileStream, new StreamReader(...) throws ArgumentException("Stream was not readable."), which surfaces as GetContentWriterArgumentError.
The StreamReader/StreamWriter pair should only be created over a stream opened with the access the check is testing; after the IOException retry the reader decision must use requestedAccess (or the retry should reopen with ReadWrite/FileShare that is guaranteed to be readable).
Expected behavior
A transient sharing violation during Set-Content/Add-Content should either fail with the real sharing-violation IOException, or be retried without leaving a write-only stream behind. It should never throw "Stream was not readable." over a stream that is legitimately write-only.
Environment
- PowerShell 7.6.5 (also observed on 5.1 and 7.x historically)
- Windows 10/11, NTFS
Summary
Set-Content/Add-Contentintermittently fail with:This is a long-standing intermittent failure (reported by users for years, e.g. https://stackoverflow.com/q/48953767, https://stackoverflow.com/q/27370389), typically when the target file is momentarily contended by another process (a concurrent writer, an antivirus scan, a file watcher, OneDrive sync).
Repro / trigger
Hard to reproduce deterministically, but a reliable trigger is having two processes write the same (usually newly created) file with
Set-Contentconcurrently, or writing a file that a file watcher / AV just opened. The error is non-deterministic but recurs under load.Root cause
In
src/System.Management.Automation/namespaces/FileSystemContentStream.cs,FileSystemContentReaderWriter.CreateStreams:FileAccess.ReadWriteso it can detect the encoding (if ((fileAccess & FileAccess.Write) != 0) fileAccess = FileAccess.ReadWrite;, ~line 838).new FileStream(path, fileMode, fileAccess, fileShare)(~line 853) throwsIOException(e.g. sharing violation because another process holds the file), which is caught (~line 856) and retried with the original write-onlyrequestedAccess(~line 866). That open succeeds.fileAccessvariable. The reader construction below still checks the upgraded value:if ((fileAccess & (FileAccess.Read)) != 0) { _reader = new StreamReader(_stream, fileEncoding); ... }(~line 873). SincefileAccessstill contains theReadbit while_streamis now a write-onlyFileStream,new StreamReader(...)throwsArgumentException("Stream was not readable."), which surfaces asGetContentWriterArgumentError.The
StreamReader/StreamWriterpair should only be created over a stream opened with the access the check is testing; after theIOExceptionretry the reader decision must userequestedAccess(or the retry should reopen withReadWrite/FileSharethat is guaranteed to be readable).Expected behavior
A transient sharing violation during
Set-Content/Add-Contentshould either fail with the real sharing-violationIOException, or be retried without leaving a write-only stream behind. It should never throw "Stream was not readable." over a stream that is legitimately write-only.Environment