RocketChat/Rocket.Chat · critical · Error

Failed to encode private key with provided password.

Error message

Failed to encode private key with provided password.

What it means

persistKeys throws this when keychain.encryptKey(private_key, password) returns a falsy value, meaning the password-based encryption of the private key failed before anything is sent to /v1/e2e.setUserPublicAndPrivateKeys. The guard exists because a falsy encoded key would otherwise persist unusable key material server-side.

Source

Thrown at apps/meteor/client/lib/e2ee/rocketchat.e2e.ts:291

	}

	removeInstanceByRoomId(rid: IRoom['_id']): void {
		delete this.instancesByRoomId[rid];
	}

	private async persistKeys(
		{ public_key, private_key }: KeyPair,
		password: string,
		{ force }: { force: boolean } = { force: false },
	): Promise<void> {
		if (typeof public_key !== 'string' || typeof private_key !== 'string') {
			throw new Error('Failed to persist keys as they are not strings.');
		}

		const encodedPrivateKey = await this.keychain.encryptKey(private_key, password);

		if (!encodedPrivateKey) {
			throw new Error('Failed to encode private key with provided password.');
		}

		await sdk.rest.post('/v1/e2e.setUserPublicAndPrivateKeys', {
			public_key,
			private_key: JSON.stringify(encodedPrivateKey),
			force,
		});
	}

	async acceptSuggestedKey(rid: string): Promise<void> {
		await sdk.rest.post('/v1/e2e.acceptSuggestedGroupKey', {
			rid,
		});
	}

	async rejectSuggestedKey(rid: string): Promise<void> {
		await sdk.rest.post('/v1/e2e.rejectSuggestedGroupKey', {
			rid,

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Verify the password argument is non-empty and is the password the keychain expects (account password in the default flow).
  2. Retry the operation once — transient crypto/derivation failures can yield a falsy result.
  3. If it persists, reset E2EE state, regenerate the key pair, and persist with { force: true }.

Example fix

// before
await e2e.persistKeys(keyPair, password);

// after
for (let attempt = 0; attempt < 2; attempt++) {
  try {
    await e2e.persistKeys(keyPair, password, { force: attempt > 0 });
    break;
  } catch (e) {
    if (attempt === 1) throw e;
    password = await promptPasswordAgain();
  }
}
Defensive patterns

Strategy: retry

Validate before calling

if (!password) {
  throw new Error('E2EE password required');
}
await e2e.persistKeys(keyPair, password);

Try / catch

let lastErr: unknown;
for (let attempt = 0; attempt < 2; attempt++) {
  try {
    await e2e.persistKeys(keyPair, password, { force: attempt > 0 });
    lastErr = undefined;
    break;
  } catch (e) {
    lastErr = e;
    password = await promptPasswordAgain(); // fresh, verified password
  }
}
if (lastErr) throw lastErr;

Prevention

When it happens

Trigger: Calling persistKeys with an empty or malformed password, when password-based key derivation inside the keychain fails (transient WebCrypto issue, non-secure context), or when a custom keychain implementation returns undefined on error instead of throwing.

Common situations: E2EE setup where the supplied password does not match expectations (account password flow), corrupted keychain state after a browser storage issue, private/incognito modes restricting crypto or storage APIs.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/168900d4aac20eb8. Report an issue: GitHub.