dotnet/wpf · error · InvalidOperationException

SR.CannotChangePublishLicense

Error message

SR.CannotChangePublishLicense

What it means

SavePublishLicense throws InvalidOperationException with SR.CannotChangePublishLicense when the publish license is written after the transform's settings have been fixed (_fixedSettings == true). Once RM encryption settings are committed (e.g. after first use/flush), they are immutable for the lifetime of the transform. This protects the integrity of the encrypted compound file.

Solutions

  1. Create a new RightsManagementEncryptionTransform instance instead of reusing the fixed one
  2. Call SavePublishLicense exactly once, before the settings are fixed
  3. Check whether _fixedSettings-equivalent state (e.g. transform already initialized and flushed) before calling SavePublishLicense
  4. Restructure code so the publish license is determined up front, prior to any encryption work

Example fix

// before
transform.SavePublishLicense(license1);
transform.SavePublishLicense(license2); // throws
// after
transform.SavePublishLicense(license1);
var transform2 = new RightsManagementEncryptionTransform();
transform2.SavePublishLicense(license2);
Defensive patterns

Strategy: validation

Validate before calling

if (transformIsFixed) throw new InvalidOperationException("Transform settings already fixed; create a new transform before calling SavePublishLicense");

Try / catch

try { transform.SavePublishLicense(license); } catch (InvalidOperationException ex) { /* recreate transform and retry once */ }

Prevention

When it happens

Trigger: Calling RightsManagementEncryptionTransform.SavePublishLicense after InitializeRMForCreate has completed and _fixedSettings was set to true; attempting to set a second publish license on the same transform instance.

Common situations: Reusing a cached RightsManagementEncryptionTransform instance across multiple protected documents; calling SavePublishLicense twice during package creation; retry logic that re-invokes initialization plus SavePublishLicense on a already-fixed transform.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/80b7be0e4a14e223. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/CompoundFile/RightsManagementEncryptionTransform.cs:198

        /// If <paramref name="publishLicense"/> is null.
        /// </exception>
        /// <exception cref="FileFormatException">
        /// If the existing RM instance data in this file cannot be updated by the current version
        /// of this class.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// If the transform settings are fixed.
        /// </exception>
        internal void
        SavePublishLicense(
            PublishLicense publishLicense
            )
        {
            ArgumentNullException.ThrowIfNull(publishLicense);

            if (_fixedSettings)
            {
                throw new InvalidOperationException(SR.CannotChangePublishLicense);
            }

            // We seek to position 0 but under the covers, the VersionedStream maintains a FormatVersion
            // structure before our logical position zero.
            _publishLicenseStream.Seek(0, SeekOrigin.Begin);

            //
            // Construct a BinaryWriter to write the rest of the instance data.
            //
            // Although BinaryWriter is IDisposable, we must not Close or Dispose it,
            // as that would close the underlying stream, which we do not own. Simply
            // allowing the BinaryWriter to be finalized after it goes out of scope
            // does -not- close the underlying stream.
            //


            // The stream is not owned by the BlockManager, therefore we cannot 
            // close the BinaryWriter, as that would Close the stream underneath.

View on GitHub (pinned to 81131a70a4)