dotnet/wpf · error · System.Xml.XmlException

SR.RelationshipTransformNotFollowedByCanonicalizationTransfo…

Error message

SR.RelationshipTransformNotFollowedByCanonicalizationTransform

What it means

Per the OPC spec, a Relationship Transform must be immediately followed by a canonicalization transform. When a Relationship transform was found but no canonicalization transform was appended after it (transform count unchanged since the Relationship entry), the parser throws RelationshipTransformNotFollowedByCanonicalizationTransform.

Solutions

  1. Append a canonicalization Transform (e.g. http://www.w3.org/2001/10/xml-exc-c14n#) immediately after the Relationship Transform and re-sign
  2. Re-sign the package with PackageDigitalSignatureManager, which emits the required transform pair
  3. Update the signing tool to the OPC-compliant transform ordering (Relationship then C14N)
  4. Validate manifest transform order against the OPC spec before distribution

Example fix

// before
<Transforms>
  <Transform Algorithm="http://schemas.openxmlformats.org/package/2006/RelationshipTransform"/>
</Transforms>
// after
<Transforms>
  <Transform Algorithm="http://schemas.openxmlformats.org/package/2006/RelationshipTransform"/>
  <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</Transforms>
Defensive patterns

Strategy: validation

Validate before calling

const string relNs = "http://schemas.openxmlformats.org/package/2006/RelationshipTransform";
var c14nSet = new HashSet<string> { "http://www.w3.org/2001/10/xml-exc-c14n#" };
bool ordered = doc.Descendants(ds + "Transforms").All(t =>
{
    var list = t.Elements(ds + "Transform").Select(x => (string)x.Attribute("Algorithm")).ToList();
    for (int i = 0; i < list.Count; i++)
        if (list[i] == relNs && (i + 1 >= list.Count || !c14nSet.Contains(list[i + 1])))
            return false;
    return true;
});
if (!ordered) throw new InvalidDataException("Relationship Transform must be followed by canonicalization");

Try / catch

try
{
    sigManager.VerifySignatures(true);
}
catch (XmlException ex) when (ex.Message.Contains("Canonicalization"))
{
    // wrong transform order: re-sign with Relationship + C14N pair
}

Prevention

When it happens

Trigger: Verify on a signature whose Reference contains a Relationship Transform as the last (or only) transform, with no canonicalization transform following it.

Common situations: Signers implementing an older or partial interpretation of the OPC signature profile; manually assembled manifests omitting the required trailing C14N transform; migrated signatures from other packaging SDKs.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/WindowsBase/MS/Internal/IO/Packaging/XmlSignatureManifest.cs:394

                                continue;   // success
                            }
                            else
                                throw new InvalidOperationException(SR.UnsupportedTransformAlgorithm);
                        }
                    }
                }
                throw new XmlException(SR.XmlSignatureParseError);
            }

            if (transforms.Count == 0)
                throw new XmlException(SR.XmlSignatureParseError);
            
            //If we found another transform after the Relationship transform, it will be validated earlier
            //in this method to make sure that its a supported xml canonicalization algorithm and so we can 
            //simplify this test condition - As per the OPC spec - Relationship transform must be followed
            //by a canonicalization algorithm.
            if (relationshipTransformFound && (transforms.Count == transformsCountWhenRelationshipTransformFound))
                throw new XmlException(SR.RelationshipTransformNotFollowedByCanonicalizationTransform);

            return transforms;
        }

        /// <summary>
        /// Parse the Relationship-specific Transform
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="partUri"></param>
        /// <param name="relationshipSelectors">may be allocated but will never be empty</param>
        private static void ParseRelationshipsTransform(XmlReader reader, Uri partUri, ref List<PackageRelationshipSelector> relationshipSelectors)
        {
            Uri owningPartUri = System.IO.Packaging.PackUriHelper.GetSourcePartUriFromRelationshipPartUri(partUri);

            // find all of the Relationship tags of form:
            //      <RelationshipReference SourceId="abc123" />
            // or 
            //      <RelationshipsGroupReference SourceType="reference-type-of-the-week" />

View on GitHub (pinned to 81131a70a4)