hashicorp/vault · warning · Error

Cross-signing a root issuer with itself must be performed ma

Error message

Cross-signing a root issuer with itself must be performed manually using the CLI.

What it means

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.

Source

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

            ...this.formData[row],
            hasError: message,
            hasUnsupportedParams: error.cause ? error.cause.map((e) => e.message).join(', ') : null,
          });
        }
      }
    })
  );

  @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,

View on GitHub (pinned to 744b611b57)

Solutions

  1. Select a different parent issuer than the intermediate being cross-signed
  2. 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)

Example fix

// before: submit, then the action throws
@action
async crossSignIntermediate(intMount, intName, newCrossSignedIssuer) {
  const existingIssuer = await this.api.secrets.pkiReadIssuer(intName, intMount);
  if (existingIssuer.issuer_id === this.args.parentIssuer.issuer_id) {
    throw new Error('Cross-signing a root issuer with itself must be performed manually using the CLI.');
  }
  ...
}

// after: guard in the form and disable self-signing up front
get isSelfSign() {
  return this.args.parentIssuer?.issuer_id === this.selectedIntermediate?.issuer_id;
}
// template: <button type="submit" disabled={{this.isSelfSign}}>
Defensive patterns

Strategy: validation

Validate before calling

// Before starting the cross-sign flow, require distinct issuers
if (parentIssuer.issuer_id === intermediateIssuer.issuer_id) {
  showInlineError('Pick a parent issuer different from the intermediate — self-signing a root must be done via the CLI');
  return;
}

Type guard

function isSelfSign(parent: { issuer_id: string }, intermediate: { issuer_id: string }): boolean {
  return parent.issuer_id === intermediate.issuer_id;
}

Try / catch

try {
  await this.crossSignIntermediate(mount, intName, newName);
} catch (e) {
  if (e.message.includes('must be performed manually using the CLI')) {
    notifyUser('Select a different parent issuer, or self-sign via: vault write pki/root/sign-intermediate');
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: Misconfigured form where both dropdowns end up on the same issuer (default selection overlaps); misunderstanding that the flow needs two distinct issuers.

Related errors


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