twentyhq/twenty · error · Error

KeyDescriptor element is missing

Error message

KeyDescriptor element is missing

What it means

Thrown by parseSAMLMetadataFromXMLFile when an IdP metadata XML parses successfully and contains an IDPSSODescriptor, but no <KeyDescriptor> child can be located. The lookup (getByPrefixAndKey) only searches a fixed namespace-prefix allowlist (md, ns0, ns2, dsig, ds) plus the bare tag name, so a valid element under a different prefix is treated as absent. KeyDescriptor is the SAML container for the IdP signing certificate.

Source

Thrown at packages/twenty-front/src/modules/settings/security/utils/parseSAMLMetadataFromXMLFile.ts:82

  | { success: true; data: z.infer<typeof validator> }
  | { success: false; reason: string } => {
  try {
    const parser = new DOMParser();
    const xmlDoc = parser.parseFromString(xmlString, 'application/xml');
    if (xmlDoc.getElementsByTagName('parsererror').length > 0) {
      throw new Error('File is not valid XML');
    }

    const entityDescriptor = getByPrefixAndKey(xmlDoc, 'EntityDescriptor');
    if (!entityDescriptor)
      throw new Error('EntityDescriptor element is missing');

    const IDPSSODescriptor = getByPrefixAndKey(xmlDoc, 'IDPSSODescriptor');
    if (!IDPSSODescriptor)
      throw new Error('IDPSSODescriptor element is missing');

    const keyDescriptors = getByPrefixAndKey(IDPSSODescriptor, 'KeyDescriptor');
    if (!keyDescriptors) throw new Error('KeyDescriptor element is missing');

    const keyInfo = getByPrefixAndKey(keyDescriptors, 'KeyInfo');
    if (!keyInfo) throw new Error('KeyInfo element is missing');

    const x509Data = getByPrefixAndKey(keyInfo, 'X509Data');
    if (!x509Data) throw new Error('X509Data element is missing');

    const x509Certificate = getByPrefixAndKey(
      x509Data,
      'X509Certificate',
    )?.textContent?.trim();
    if (!x509Certificate)
      throw new Error('X509Certificate is missing or empty');

    const singleSignOnServices = getAllByPrefixAndKey(
      IDPSSODescriptor,
      'SingleSignOnService',
    ).map((service) => ({

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Confirm the uploaded file is the IdP metadata (must contain <EntityDescriptor> with an <IDPSSODescriptor>).
  2. Open the XML and verify a <KeyDescriptor> element exists as a direct child of <IDPSSODescriptor>.
  3. If KeyDescriptor uses an unusual namespace prefix, add that prefix to the `allPrefix` array in parseSAMLMetadataFromXMLFile.ts or re-export metadata from the IdP with a standard prefix (md/ds).

Example fix

// before: <ns5:KeyDescriptor use="signing"> ... </ns5:KeyDescriptor> (prefix ns5 not in allowlist)
// after:  <md:KeyDescriptor use="signing"> ... </md:KeyDescriptor>
Defensive patterns

Strategy: validation

Validate before calling

const validateIdPMetadataShape = (xml: string): string[] => {
  const doc = new DOMParser().parseFromString(xml, 'application/xml');
  if (doc.getElementsByTagName('parsererror').length) return ['File is not valid XML'];
  const prefixes = ['md', 'ns0', 'ns2', 'dsig', 'ds', ''];
  const has = (parent: Document | Element, tag: string) =>
    prefixes.some((p) => parent.getElementsByTagName(p ? `${p}:${tag}` : tag).length > 0);
  const errs: string[] = [];
  if (!has(doc, 'EntityDescriptor')) errs.push('EntityDescriptor element is missing');
  if (!has(doc, 'IDPSSODescriptor')) errs.push('IDPSSODescriptor element is missing');
  if (!has(doc, 'KeyDescriptor')) errs.push('KeyDescriptor element is missing');
  return errs;
};

Type guard

const isLikelyIdPMetadata = (xml: string): boolean => {
  const doc = new DOMParser().parseFromString(xml, 'application/xml');
  return doc.getElementsByTagName('parsererror').length === 0
    && /IDPSSODescriptor/.test(xml)
    && /KeyDescriptor/.test(xml);
};

Try / catch

// parseSAMLMetadataFromXMLFile already returns { success, reason }; use that
const res = parseSAMLMetadataFromXMLFile(xml);
if (!res.success) {
  showFormError(res.reason); // e.g. 'KeyDescriptor element is missing'
  return;
}

Prevention

When it happens

Trigger: Uploading an XML file that is valid SAML metadata and has IDPSSODescriptor but no KeyDescriptor inside it (e.g., SP metadata, or an IdP that omits the signing role). Also triggered when KeyDescriptor exists but uses a namespace prefix not in the allowlist (md/ns0/ns2/dsig/ds), since getByPrefixAndKey will not match it.

Common situations: User uploads the Service Provider metadata XML by mistake instead of the Identity Provider metadata. IdP exports metadata with a custom/uncommon namespace prefix. Metadata is for a different role descriptor (e.g., AttributeAuthority) that has no KeyDescriptor.

Related errors


AI-assisted analysis of twentyhq/twenty@1f5dd2bbd2 (2026-08-12). Data as JSON: /api/errors/65009e807d131aa7. Report an issue: GitHub.