dotnet/wpf · error · IOException

SR.PackagingWriteableDelegateGaveNullPart

Error message

SR.PackagingWriteableDelegateGaveNullPart

What it means

WriteableOnDemandPackagePart.WriteableStreamFactory asks a user-supplied delegate (_getWriteablePartInstance) for the actual PackagePart to write into, and the delegate returned null. The library throws IOException because it cannot proceed with a lazy writeable part without a concrete backing part.

Solutions

  1. Fix the _getWriteablePartInstance delegate so it always returns a valid PackagePart or throws a descriptive exception instead of returning null.
  2. Verify the underlying package is still open and the part Uri/access requested are valid when the delegate runs.
  3. Guard the write path: only write to the on-demand stream after confirming the delegate can produce the part.

Example fix

// before
PackagePart GetWritablePart(WriteableOnDemandPackagePart p)
{
    if (_package == null) return null; // causes IOException
    return _package.GetPart(p.Uri);
}
// after
PackagePart GetWritablePart(WriteableOnDemandPackagePart p)
{
    if (_package == null)
        throw new IOException("Package already closed; cannot create writeable part for " + p.Uri);
    return _package.GetPart(p.Uri);
}
Defensive patterns

Strategy: validation

Validate before calling

if (_package == null || _package.FileOpenAccess != FileAccess.ReadWrite)
    throw new IOException("Package must be open for ReadWrite before writeable part can be created.");

Type guard

bool CanProvideWritablePart(Func<PackagePart> factory) { try { return factory?.Invoke() != null; } catch { return false; } }

Try / catch

try { stream.Write(data, 0, data.Length); }
catch (IOException ex) when (ex.Message.Contains("WriteableDelegate"))
{ Log.Error("Part delegate returned null", ex); }

Prevention

When it happens

Trigger: Calling Write/SetLength on a WriteableOnDemandPackagePart's stream triggers EnsureWritable -> _writeableStreamFactory(mode, access); the registered delegate callback returns null instead of a PackagePart instance.

Common situations: Custom XPS document assembly/deferred-save code where the delegate that instantiates the real part fails to find or create the part (e.g. part already disposed, package closed, delegate logic returning null on error paths instead of throwing).

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationUI/MS/Internal/Documents/Application/WriteableOnDemandPackagePart.cs:192

    /// <param name="mode">Desired FileMode.</param>
    /// <param name="access">Desired FileAccess.</param>
    /// <returns>A writeable stream.</returns>
    private Stream WriteableStreamFactory(
        FileMode mode,
        FileAccess access)
    {
        if (!_isActiveWriteable)
        {
            Trace.SafeWrite(
                Trace.Packaging,
                "Creating a writeable stream for {0} with {1} access",
                _activePart.Uri,
                access);

            PackagePart writingPart = _getWriteablePartInstance(this);
            if (writingPart == null)
            {
                throw new IOException(
                    SR.PackagingWriteableDelegateGaveNullPart);
            }

            if (writingPart.Equals(this))
            {
                throw new IOException(
                    SR.PackagingCircularReference);
            }

            _activePart = writingPart;
            _isActiveWriteable = true;
        }

        return _activePart.GetStream(mode, access);
    }
    #endregion Private Methods

    #region Private Fields

View on GitHub (pinned to 81131a70a4)