{"record":{"id":"634ddc589be7b1ef","repo":"hashicorp/vault","slug":"cross-signing-a-root-issuer-with-itself-must-be-pe","errorCode":null,"errorMessage":"Cross-signing a root issuer with itself must be performed manually using the CLI.","messagePattern":"Cross-signing a root issuer with itself must be performed manually using the CLI\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"ui/lib/pki/addon/components/pki-issuer-cross-sign.js","lineNumber":137,"sourceCode":"            ...this.formData[row],\n            hasError: message,\n            hasUnsupportedParams: error.cause ? error.cause.map((e) => e.message).join(', ') : null,\n          });\n        }\n      }\n    })\n  );\n\n  @action\n  async crossSignIntermediate(intMount, intName, newCrossSignedIssuer) {\n    const { parentIssuer } = this.args;\n    // 1. Fetch issuer we want to sign\n    // What/Recovery: any failure is early enough that you can bail safely/normally.\n    const existingIssuer = await this.api.secrets.pkiReadIssuer(intName, intMount);\n\n    // Return if user is attempting to self-sign issuer\n    if (existingIssuer.issuer_id === parentIssuer.issuer_id) {\n      throw new Error('Cross-signing a root issuer with itself must be performed manually using the CLI.');\n    }\n\n    // Translate certificate values to API parameters to pass along: CSR -> Signed CSR -> Cross-Signed issuer\n    // some of these values do not apply to a CSR, but pass anyway. If there is any issue parsing the certificate,\n    // (ex. the certificate contains unsupported values) direct user to manually cross-sign via CLI\n    const certData = parseCertificate(existingIssuer.certificate);\n    if (certData.parsing_errors.length > 0) {\n      throw new Error('Certificate must be manually cross-signed using the CLI.', {\n        cause: certData.parsing_errors,\n      });\n    }\n\n    // 2. Create the new CSR\n    // What/Recovery: any failure is early enough that you can bail safely/normally.\n    const { csr } = await this.api.secrets.pkiGenerateIntermediate('existing', intMount, {\n      key_ref: existingIssuer.key_id,\n      common_name: existingIssuer.common_name,\n      ...certData,","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/hashicorp/vault/blob/744b611b5700b3b7f82d76b4d6938a91b9989367/ui/lib/pki/addon/components/pki-issuer-cross-sign.js#L119-L155","documentation":"Thrown by the PKI cross-sign component (ui/lib/pki/addon/components/pki-issuer-cross-sign.js:137) in crossSignIntermediate(). After fetching the target intermediate issuer via pkiReadIssuer, it compares issuer_id against the selected parent issuer; if they are identical the UI refuses because cross-signing a root with itself is not supported in the UI and must be done with the CLI.","triggerScenarios":"In the cross-sign issuers workflow, selecting the same issuer as both the intermediate to sign and the parent signing issuer — i.e. attempting to self-sign a root — then submitting.","commonSituations":"Misconfigured form where both dropdowns end up on the same issuer (default selection overlaps); misunderstanding that the flow needs two distinct issuers.","solutions":["Select a different parent issuer than the intermediate being cross-signed","If you truly need to self-sign a root, do it manually: vault write pki/root/sign-intermediate ... (or pki issuer sign-intermediate on newer versions)"],"exampleFix":"// before: submit, then the action throws\n@action\nasync crossSignIntermediate(intMount, intName, newCrossSignedIssuer) {\n  const existingIssuer = await this.api.secrets.pkiReadIssuer(intName, intMount);\n  if (existingIssuer.issuer_id === this.args.parentIssuer.issuer_id) {\n    throw new Error('Cross-signing a root issuer with itself must be performed manually using the CLI.');\n  }\n  ...\n}\n\n// after: guard in the form and disable self-signing up front\nget isSelfSign() {\n  return this.args.parentIssuer?.issuer_id === this.selectedIntermediate?.issuer_id;\n}\n// template: <button type=\"submit\" disabled={{this.isSelfSign}}>","handlingStrategy":"validation","validationCode":"// Before starting the cross-sign flow, require distinct issuers\nif (parentIssuer.issuer_id === intermediateIssuer.issuer_id) {\n  showInlineError('Pick a parent issuer different from the intermediate — self-signing a root must be done via the CLI');\n  return;\n}","typeGuard":"function isSelfSign(parent: { issuer_id: string }, intermediate: { issuer_id: string }): boolean {\n  return parent.issuer_id === intermediate.issuer_id;\n}","tryCatchPattern":"try {\n  await this.crossSignIntermediate(mount, intName, newName);\n} catch (e) {\n  if (e.message.includes('must be performed manually using the CLI')) {\n    notifyUser('Select a different parent issuer, or self-sign via: vault write pki/root/sign-intermediate');\n  } else {\n    throw e;\n  }\n}","preventionTips":["Disable the submit button when both issuer selectors point to the same issuer","Default the parent dropdown to a value distinct from the selected intermediate"],"tags":["pki","cross-signing","issuer","validation"],"backgroundTag":null,"analyzedSha":"744b611b5700b3b7f82d76b4d6938a91b9989367","analyzedAt":"2026-08-15T14:40:29.333Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}