{"record":{"id":"24ce5eadbf1b35ac","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-keys","errorCode":"error-invalid-keys","errorMessage":"Invalid keys","messagePattern":"Invalid keys","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/meteor-methods/platform/setUserPublicAndPrivateKeys.ts","lineNumber":56,"sourceCode":"\tconst subscribedRoomIds = await Rooms.getSubscribedRoomIdsWithoutE2EKeys(userId);\n\tawait Rooms.addUserIdToE2EEQueueByRoomIds(subscribedRoomIds, userId);\n\n\tvoid notifyOnRoomChangedById(subscribedRoomIds);\n};\n\nMeteor.methods<ServerMethods>({\n\tasync 'e2e.setUserPublicAndPrivateKeys'(keyPair) {\n\t\tmethodDeprecationLogger.method('e2e.setUserPublicAndPrivateKeys', '9.0.0', '/v1/e2e.setUserPublicAndPrivateKeys');\n\t\tconst userId = Meteor.userId();\n\n\t\tif (!userId) {\n\t\t\tthrow new Meteor.Error('error-invalid-user', 'Invalid user', {\n\t\t\t\tmethod: 'e2e.setUserPublicAndPrivateKeys',\n\t\t\t});\n\t\t}\n\n\t\tif (!keyPair.public_key || !keyPair.private_key) {\n\t\t\tthrow new Meteor.Error('error-invalid-keys', 'Invalid keys', {\n\t\t\t\tmethod: 'e2e.setUserPublicAndPrivateKeys',\n\t\t\t});\n\t\t}\n\n\t\tawait setUserPublicAndPrivateKeysMethod(userId, keyPair);\n\t},\n});\n","sourceCodeStart":38,"sourceCodeEnd":64,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/meteor-methods/platform/setUserPublicAndPrivateKeys.ts#L38-L64","documentation":"Thrown by the 'e2e.setUserPublicAndPrivateKeys' Meteor method when the keyPair argument lacks a truthy public_key or private_key field. Rocket.Chat calls this once per user during end-to-end-encryption setup to persist the generated RSA key pair. The guard is a plain truthiness check, so missing fields, empty strings, null, or undefined in either slot are rejected before setUserPublicAndPrivateKeysMethod runs. The method is deprecated since 9.0.0 in favor of POST /v1/e2e.setUserPublicAndPrivateKeys.","triggerScenarios":"Calling Meteor.callAsync('e2e.setUserPublicAndPrivateKeys', keyPair) where keyPair.public_key or keyPair.private_key is missing/empty/undefined - typically because the browser's RSA key-pair generation failed or had not finished before the call, or the client built the object with wrong field names (publicKey instead of public_key).","commonSituations":"E2EE setup races where the method fires before key generation completes; corrupted or wiped local E2E key storage; custom clients or tests hand-crafting the keyPair payload; key export returning an empty string after subprocess/browser quirks.","solutions":["Validate that keyPair has non-empty snake_case string fields public_key and private_key before invoking the method.","On the client, await full key-pair generation and confirm the exported PEM/base64 strings are non-empty before calling.","If local E2E storage is corrupted, clear the client's stored e2e keys and restart the key-generation flow.","On migrated servers, confirm the REST equivalent /v1/e2e.setUserPublicAndPrivateKeys receives the same payload shape."],"exampleFix":"// before\nMeteor.callAsync('e2e.setUserPublicAndPrivateKeys', keyPair);\n\n// after\nif (!keyPair?.public_key || !keyPair?.private_key) {\n\tthrow new Error('E2E key pair incomplete - regenerate before saving');\n}\nawait Meteor.callAsync('e2e.setUserPublicAndPrivateKeys', keyPair);","handlingStrategy":"validation","validationCode":"const hasValidKeyPair = (kp: { public_key?: unknown; private_key?: unknown }): boolean =>\n\ttypeof kp?.public_key === 'string' && (kp.public_key as string).length > 0 &&\n\ttypeof kp?.private_key === 'string' && (kp.private_key as string).length > 0;\n\nif (!hasValidKeyPair(keyPair)) {\n\tthrow new Error('E2E key pair incomplete - regenerate keys before saving');\n}\nawait Meteor.callAsync('e2e.setUserPublicAndPrivateKeys', keyPair);","typeGuard":"interface E2EKeyPair { public_key: string; private_key: string }\nfunction isE2EKeyPair(v: unknown): v is E2EKeyPair {\n\tif (typeof v !== 'object' || v === null) return false;\n\tconst kp = v as Record<string, unknown>;\n\treturn typeof kp.public_key === 'string' && kp.public_key.length > 0 &&\n\t\ttypeof kp.private_key === 'string' && kp.private_key.length > 0;\n}","tryCatchPattern":"try {\n\tawait Meteor.callAsync('e2e.setUserPublicAndPrivateKeys', keyPair);\n} catch (e: any) {\n\tif (e?.error === 'error-invalid-keys') {\n\t\t// regenerate the pair client-side, then retry once\n\t}\n}","preventionTips":["Await key-pair generation fully before persisting keys.","Never hand-build the keyPair object; use the generator's exact snake_case output.","Log the exported key lengths (not contents) during E2EE onboarding to catch empty exports early."],"tags":["e2ee","meteor-methods","validation","encryption"],"backgroundTag":"invalid-request-payload","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}