twentyhq/twenty · error · Error

X509Certificate is missing or empty

Error message

X509Certificate is missing or empty

What it means

Thrown when <X509Data> exists but either has no <X509Certificate> child, or the child's textContent is empty after trimming. The certificate text is the base64 DER blob Twenty uses as the IdP signing cert.

Source

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

    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) => ({
      binding: service.getAttribute('Binding'),
      location: service.getAttribute('Location'),
    }));

    // Prefer HTTP-Redirect (the default authnRequestBinding on the SP side),
    // fall back to HTTP-POST since both are valid SAML 2.0 bindings and many
    // IdPs (e.g. JumpCloud) only advertise HTTP-POST.
    const ssoUrl =
      singleSignOnServices.find((s) => s.binding === HTTP_REDIRECT_BINDING)
        ?.location ??
      singleSignOnServices.find((s) => s.binding === HTTP_POST_BINDING)
        ?.location;

View on GitHub (pinned to 1f5dd2bbd2)

Solutions

  1. Open the XML and confirm <X509Certificate> has non-empty base64 text content.
  2. Re-export metadata from the IdP so the cert body is intact.
  3. Validate the cert string decodes as valid base64 before uploading.

Example fix

// before: <ds:X509Certificate></ds:X509Certificate>
// after:  <ds:X509Certificate>MIIDAzCCAeugAwIBAgIU...</ds:X509Certificate>
Defensive patterns

Strategy: validation

Validate before calling

const extractCert = (xml: string): string | null => {
  const m = xml.match(/<[^>]*X509Certificate[^>]*>([\s\S]*?)<\/[^>]*X509Certificate>/);
  const body = m?.[1]?.trim();
  return body ? body : null;
};
// guard: if (!extractCert(xml)) showFormError('certificate missing');

Type guard

const isNonEmptyCertificate = (cert: string | undefined | null): cert is string =>
  typeof cert === 'string' && cert.trim().length > 0 && /^[A-Za-z0-9+/=\s]+$/.test(cert);

Try / catch

const res = parseSAMLMetadataFromXMLFile(xml);
if (!res.success) {
  // res.reason may be 'X509Certificate is missing or empty'
  return;
}

Prevention

When it happens

Trigger: X509Certificate element is missing entirely, is self-closing, or its text content is whitespace/empty. A placeholder metadata file was uploaded.

Common situations: Test/stub metadata with an empty cert. Copy-paste dropped the long base64 string. IdP certificate rotation left a temporarily empty element.

Understand the failure class

Related errors


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