n8n-io/n8n · error · Error

The Certificate field must contain a PEM certificate (-----B

Error message

The Certificate field must contain a PEM certificate (-----BEGIN CERTIFICATE-----).

What it means

Thrown by certificateThumbprint in buildClientAssertion when `new X509Certificate(formatPemBlock(cert))` raises. The wrapper message tells the caller the certificate field must be a PEM certificate; the original crypto error is preserved as `cause`. The certificate is used to compute the x5t (SHA-1 thumbprint) JWT header.

Source

Thrown at packages/@n8n/utils/src/client-assertion.ts:27

import { formatPemBlock } from './format-pem-block';

// private_key_jwt (RFC 7521/7523): the client proves its identity with a JWT
// signed by its private key instead of a shared secret. The `x5t` header (SHA-1
// thumbprint of the certificate) tells the server which public key verifies it.
export const CLIENT_ASSERTION_TYPE = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer';

const ASSERTION_TTL_SECONDS = 300;

function base64url(input: Buffer | string): string {
	return Buffer.from(input).toString('base64url');
}

function certificateThumbprint(certificate: string): string {
	let parsed: X509Certificate;
	try {
		parsed = new X509Certificate(formatPemBlock(certificate));
	} catch (error) {
		throw new Error(
			'The Certificate field must contain a PEM certificate (-----BEGIN CERTIFICATE-----).',
			{ cause: error },
		);
	}
	return Buffer.from(parsed.fingerprint.replace(/:/g, ''), 'hex').toString('base64url');
}

export interface BuildClientAssertionOptions {
	clientId: string;
	/** Token endpoint; used as the JWT `aud`. */
	accessTokenUri: string;
	/** RSA private key (PEM). Signing is RS256-only; EC/Ed25519 keys are not supported. */
	privateKey: string;
	certificate: string;
}

export function buildClientAssertion(options: BuildClientAssertionOptions): string {
	const now = Math.floor(Date.now() / 1000);

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Verify the certificate begins with '-----BEGIN CERTIFICATE-----' and ends with the matching END marker.
  2. Ensure you are passing the public certificate, not the private key.
  3. Validate with `openssl x509 -in cert.pem -noout` before configuring.
  4. Inspect err.cause for the underlying crypto reason to pinpoint the format issue.

Example fix

// before
buildClientAssertion({ ..., certificate: privateKeyPem }); // wrong field content
// after
buildClientAssertion({ ..., certificate: publicCertPem }); // -----BEGIN CERTIFICATE-----...
Defensive patterns

Strategy: validation

Validate before calling

function assertValidCertificatePem(cert: string): void {
  if (!cert || !/-----BEGIN CERTIFICATE-----/.test(cert) || !/-----END CERTIFICATE-----/.test(cert)) {
    throw new Error('certificate must be a PEM string bounded by BEGIN/END CERTIFICATE markers');
  }
  // optionally verify it parses:
  new X509Certificate(cert); // throws on malformed input
}

Type guard

function looksLikePemCertificate(v: unknown): v is string {
  return typeof v === 'string'
    && /-----BEGIN CERTIFICATE-----[\s\S]*-----END CERTIFICATE-----/.test(v);
}

Try / catch

try {
  return buildClientAssertion(opts);
} catch (e) {
  if (/must contain a PEM certificate/i.test(e?.message ?? '')) {
    throw new ConfigError('certificate field is not a valid PEM certificate', { cause: e });
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing an empty certificate string, a private key PEM instead of a certificate PEM, a DER/base64 blob without the BEGIN CERTIFICATE markers, a certificate for the wrong key, or a malformed/truncated PEM.

Common situations: Uploading the private key into the certificate field by mistake in a config UI; copy-paste truncating the END marker; using a self-signed cert in a format Node's crypto cannot parse; passing a cert chain where a single leaf is expected without formatPemBlock-friendly input.

Understand the failure class

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/edd1262ccc8e176e. Report an issue: GitHub.