dotnet/wpf · error · InvalidOperationException
Cannot sign read-only file.
Error message
Cannot sign read-only file.
What it means
Sign throws InvalidOperationException (SR.CannotSignReadOnlyFile) when the package was opened in read-only mode, because signing requires writing the signature part and digital signature origin part back into the package.
Solutions
- Open the package with PackageOpenMode.ReadWrite (or FileAccess.ReadWrite) before signing
- Ensure the underlying file/stream and its directory grant write permission
- If the package is only for consumption, do not call Sign — check ReadOnly property first and skip signing
- Copy the read-only package to a writable location and reopen ReadWrite if you must sign it
Example fix
// before
using (Package pkg = Package.Open(path, FileMode.Open, FileAccess.Read))
{
var mgr = new PackageDigitalSignatureManager(pkg);
mgr.Sign(parts, cert); // throws: package is read-only
}
// after
using (Package pkg = Package.Open(path, FileMode.Open, FileAccess.ReadWrite))
{
var mgr = new PackageDigitalSignatureManager(pkg);
if (!mgr.ReadOnly)
mgr.Sign(parts, cert);
} Defensive patterns
Strategy: validation
Validate before calling
if (manager.ReadOnly)
throw new InvalidOperationException("Open the package with ReadWrite access before signing."); Try / catch
try { manager.Sign(parts, cert); }
catch (InvalidOperationException ex) when (manager.ReadOnly) { log.Error("Package is read-only", ex); } Prevention
- Always open packages with FileAccess.ReadWrite when signing is expected
- Check PackageDigitalSignatureManager.ReadOnly before sign operations
- Verify file/share permissions in deployment environments
When it happens
Trigger: Creating a Package or the manager from a stream/file opened FileAccess.Read or PackageOpenMode.ReadOnly and then calling any Sign/Countersign overload.
Common situations: Opening a package from a stream that was opened with FileAccess.Read; consuming a signed package from disk without write permission and then attempting to re-sign; opening from a read-only network share or embedded resource stream.
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
- Cannot countersign an unsigned package.
- Cannot remove signature from read-only file.
- NotSupportedException
- SR.Format(SR.MemberNotAllowedForView, "AddNew")
- SR.Format(SR.MemberNotAllowedForView, "AddNewItem")
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/44232e0f57dfcca7.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/System/IO/Packaging/PackageDigitalSignatureManager.cs:445
/// tags, it follows that this Manifest tag must include at least one Reference tag.
/// This means that every signature include at least one of a Part to sign (non-empty parts tag)
/// or a Relationship to sign (non-empty relationshipSelectors) even if such a signature
/// is only destined to sign signatureObjects and/or objectReferences.
/// This overload provides support for generation of Xml signatures that require custom
/// Object tags. For any provided Object tag to be signed, a corresponding Reference
/// tag must be provided with a Uri that targets the Object tag using local fragment
/// syntax. If the object had an ID of "myObject" the Uri on the Reference would
/// be "#myObject". For unsigned objects, no reference is required.</remarks>
public PackageDigitalSignature Sign(
IEnumerable<Uri> parts,
X509Certificate certificate,
IEnumerable<PackageRelationshipSelector> relationshipSelectors,
String signatureId,
IEnumerable<System.Security.Cryptography.Xml.DataObject> signatureObjects,
IEnumerable<System.Security.Cryptography.Xml.Reference> objectReferences)
{
if (ReadOnly)
throw new InvalidOperationException(SR.CannotSignReadOnlyFile);
VerifySignArguments(parts, certificate, relationshipSelectors, signatureId, signatureObjects, objectReferences);
// substitute default id if none given
if (string.IsNullOrEmpty(signatureId))
{
signatureId = "packageSignature"; // default
}
// Make sure the list reflects what's in the package.
// Do this before adding the new signature part because we don't want it included until it
// is fully formed (and delaying the add saves us having to remove it in case there is an
// error during the Sign call).
EnsureSignatures();
Uri newSignaturePartName = GenerateSignaturePartName();
if (_container.PartExists(newSignaturePartName))
throw new ArgumentException(SR.DuplicateSignature);View on GitHub (pinned to 81131a70a4)