Set-Content/Add-Content intermittently fail: "Stream was not readable" (GetContentWriterArgumentError) after IOException retry leaves write-only stream · Issue #27947 · PowerShell/PowerShell · GitHub
Skip to content

Set-Content/Add-Content intermittently fail: "Stream was not readable" (GetContentWriterArgumentError) after IOException retry leaves write-only stream #27947

Description

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:

  1. 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).
  2. 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.
  3. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions