hashicorp/vault · warning · Error

Certificate must be manually cross-signed using the CLI.

Error message

Certificate must be manually cross-signed using the CLI.

What it means

Thrown by the PKI cross-sign component (ui/lib/pki/addon/components/pki-issuer-cross-sign.js:145). Before generating a CSR, the component parses the intermediate issuer's certificate with parseCertificate() to translate its values (CN, TTLs, etc.) into API parameters. If the parse reports any parsing_errors, the UI cannot faithfully reproduce the certificate parameters and directs the user to cross-sign manually via the CLI (the original errors are attached as cause).

Source

Thrown at ui/lib/pki/addon/components/pki-issuer-cross-sign.js:145

  @action
  async crossSignIntermediate(intMount, intName, newCrossSignedIssuer) {
    const { parentIssuer } = this.args;
    // 1. Fetch issuer we want to sign
    // What/Recovery: any failure is early enough that you can bail safely/normally.
    const existingIssuer = await this.api.secrets.pkiReadIssuer(intName, intMount);

    // Return if user is attempting to self-sign issuer
    if (existingIssuer.issuer_id === parentIssuer.issuer_id) {
      throw new Error('Cross-signing a root issuer with itself must be performed manually using the CLI.');
    }

    // Translate certificate values to API parameters to pass along: CSR -> Signed CSR -> Cross-Signed issuer
    // some of these values do not apply to a CSR, but pass anyway. If there is any issue parsing the certificate,
    // (ex. the certificate contains unsupported values) direct user to manually cross-sign via CLI
    const certData = parseCertificate(existingIssuer.certificate);
    if (certData.parsing_errors.length > 0) {
      throw new Error('Certificate must be manually cross-signed using the CLI.', {
        cause: certData.parsing_errors,
      });
    }

    // 2. Create the new CSR
    // What/Recovery: any failure is early enough that you can bail safely/normally.
    const { csr } = await this.api.secrets.pkiGenerateIntermediate('existing', intMount, {
      key_ref: existingIssuer.key_id,
      common_name: existingIssuer.common_name,
      ...certData,
    });
    // 3. Sign newCSR with correct parent to create cross-signed cert, "issuing"
    // an intermediate certificate.
    // What/Recovery: any failure is early enough that you can bail safely/normally.
    const issuerRef = parentIssuer.issuer_name || parentIssuer.issuer_id;
    const { ca_chain } = await this.api.secrets.pkiIssuerSignIntermediate(
      issuerRef,
      this.secretMountPath.currentPath,

View on GitHub (pinned to 744b611b57)

Solutions

  1. Follow the message: cross-sign manually with the CLI — generate a CSR (pki/issuer/.../generate-csr or pki_intermediate set-signed workflow), sign it with the parent root, then import the signed bundle
  2. Check the error's cause field for the exact parsing_errors reported by parseCertificate to see which certificate value is unsupported
  3. If the certificate is under your control, reissue it without the offending values and retry the UI flow
Defensive patterns

Strategy: validation

Validate before calling

// Parse up front and bail with actionable detail instead of mid-flow
const certData = parseCertificate(issuer.certificate);
if (certData.parsing_errors.length > 0) {
  showManualInstructions(`Certificate values unsupported by the UI: ${certData.parsing_errors.join('; ')}`);
  return;
}

Type guard

interface ParsedCertificate {
  parsing_errors: string[];
  [key: string]: unknown;
}
function isCleanParse(cert: ParsedCertificate): boolean {
  return Array.isArray(cert.parsing_errors) && cert.parsing_errors.length === 0;
}

Try / catch

try {
  await this.crossSignIntermediate(mount, intName, newName);
} catch (e) {
  if (e.message === 'Certificate must be manually cross-signed using the CLI.') {
    // e.cause holds the parseError.js parsing_errors array — show it
    notifyUser(`Cross-sign manually via CLI. Unsupported values: ${e.cause?.join?.('; ')}`);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: The intermediate issuer's certificate contains values the UI certificate parser cannot interpret — unusual extensions, exotic key types, or otherwise unsupported fields — so certData.parsing_errors is non-empty.

Common situations: Issuers created outside Vault or by older/other CAs with non-standard extensions; certificates using curves or SAN patterns the parser does not model.

Understand the failure class

Related errors


AI-assisted analysis of hashicorp/vault@744b611b57 (2026-08-15). Data as JSON: /api/errors/62dfa7f685d6e2a3. Report an issue: GitHub.