{"record":{"id":"a271f59884b37bb8","repo":"twentyhq/twenty","slug":"x509certificate-is-missing-or-empty","errorCode":null,"errorMessage":"X509Certificate is missing or empty","messagePattern":"X509Certificate is missing or empty","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/twenty-front/src/modules/settings/security/utils/parseSAMLMetadataFromXMLFile.ts","lineNumber":95,"sourceCode":"    const IDPSSODescriptor = getByPrefixAndKey(xmlDoc, 'IDPSSODescriptor');\n    if (!IDPSSODescriptor)\n      throw new Error('IDPSSODescriptor element is missing');\n\n    const keyDescriptors = getByPrefixAndKey(IDPSSODescriptor, 'KeyDescriptor');\n    if (!keyDescriptors) throw new Error('KeyDescriptor element is missing');\n\n    const keyInfo = getByPrefixAndKey(keyDescriptors, 'KeyInfo');\n    if (!keyInfo) throw new Error('KeyInfo element is missing');\n\n    const x509Data = getByPrefixAndKey(keyInfo, 'X509Data');\n    if (!x509Data) throw new Error('X509Data element is missing');\n\n    const x509Certificate = getByPrefixAndKey(\n      x509Data,\n      'X509Certificate',\n    )?.textContent?.trim();\n    if (!x509Certificate)\n      throw new Error('X509Certificate is missing or empty');\n\n    const singleSignOnServices = getAllByPrefixAndKey(\n      IDPSSODescriptor,\n      'SingleSignOnService',\n    ).map((service) => ({\n      binding: service.getAttribute('Binding'),\n      location: service.getAttribute('Location'),\n    }));\n\n    // Prefer HTTP-Redirect (the default authnRequestBinding on the SP side),\n    // fall back to HTTP-POST since both are valid SAML 2.0 bindings and many\n    // IdPs (e.g. JumpCloud) only advertise HTTP-POST.\n    const ssoUrl =\n      singleSignOnServices.find((s) => s.binding === HTTP_REDIRECT_BINDING)\n        ?.location ??\n      singleSignOnServices.find((s) => s.binding === HTTP_POST_BINDING)\n        ?.location;\n","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/twentyhq/twenty/blob/1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6/packages/twenty-front/src/modules/settings/security/utils/parseSAMLMetadataFromXMLFile.ts#L77-L113","documentation":"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.","triggerScenarios":"X509Certificate element is missing entirely, is self-closing, or its text content is whitespace/empty. A placeholder metadata file was uploaded.","commonSituations":"Test/stub metadata with an empty cert. Copy-paste dropped the long base64 string. IdP certificate rotation left a temporarily empty element.","solutions":["Open the XML and confirm <X509Certificate> has non-empty base64 text content.","Re-export metadata from the IdP so the cert body is intact.","Validate the cert string decodes as valid base64 before uploading."],"exampleFix":"// before: <ds:X509Certificate></ds:X509Certificate>\n// after:  <ds:X509Certificate>MIIDAzCCAeugAwIBAgIU...</ds:X509Certificate>","handlingStrategy":"validation","validationCode":"const extractCert = (xml: string): string | null => {\n  const m = xml.match(/<[^>]*X509Certificate[^>]*>([\\s\\S]*?)<\\/[^>]*X509Certificate>/);\n  const body = m?.[1]?.trim();\n  return body ? body : null;\n};\n// guard: if (!extractCert(xml)) showFormError('certificate missing');","typeGuard":"const isNonEmptyCertificate = (cert: string | undefined | null): cert is string =>\n  typeof cert === 'string' && cert.trim().length > 0 && /^[A-Za-z0-9+/=\\s]+$/.test(cert);","tryCatchPattern":"const res = parseSAMLMetadataFromXMLFile(xml);\nif (!res.success) {\n  // res.reason may be 'X509Certificate is missing or empty'\n  return;\n}","preventionTips":["Validate the cert body is non-empty base64 before upload.","Re-export metadata after cert rotation to avoid placeholder/empty elements."],"tags":["saml","sso","xml","enterprise","validation"],"backgroundTag":null,"analyzedSha":"1f5dd2bbd2a8da3419c8cfd52dd545c0024df1a6","analyzedAt":"2026-08-12T15:37:27.593Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}