Keys and Juicebox
Unlock fails (invalid PIN)
- Confirm the PIN matches the one used with
setup - Wait between attempts; realms rate-limit wrong guesses and can lock recovery after too many failures
- Python
- TypeScript
- Rust
- Go
- C#
- Java
Encrypt or decrypt fails because keys are not loaded
Load private keys first, then set the public-key version from your record on X.- Python
- TypeScript
- Rust
- Go
- C#
- Java
Missing conversation key for a message
You do not have the raw key for that message’sconversation_key_version.
- Decrypt key material from
conversation_key_change_event(live events) ormeta.conversation_key_events(history) withextract_conversation_keys, or include those blobs indecrypt_events - Confirm conversation keys were added for that version and you are still a participant (see Getting Started)
Peer has no public keys
They may not have finished onboarding. After they register, loadpublic_key, signing_public_key, identity_public_key_signature, and public_key_version from API reference → Encryption keys.
Decryption and signatures
Decrypt fails
- Stale or wrong raw conversation key, or wrong key version
- Incomplete
encoded_eventstring - Event type is not an encrypted message you can treat as decryptable content
Signature does not verify
Verification is fail-closed by default (reject_unverified = true): the SDK already rejects unverified signed events, so a failure here means the verification inputs are wrong, not that you need to turn checking on. Common causes:
- Missing or incomplete signing-key entry for the sender (all fields required by the Chat XDK—see the Chat XDK reference)
- The sender rotated versions—re-fetch their public keys
- A key version below the accepted floor never verifies
set_reject_unverified setter exists to opt out of this default (false, not recommended). If you disabled it earlier, restore the fail-closed default:
- Python
- TypeScript
- Rust
- Go
- C#
- Java
Old events permanently fail verification
Errors likesignature missing or no matching signing key or an ECDSA mismatch on old events are permanent. Signatures are immutable and verified by rebuilding the signed payload from the event itself, so an event that was signed over different bytes (or never signed) fails on every future load—no retry, key refresh, or API call can heal it. Treat these events as tombstones, not retryable errors. Rotating the conversation key starts a clean, verifiable history from that point forward; new messages are unaffected.
Building the send payload
These mistakes are specific to X Chat encryption (not general HTTP errors):API returns 400 for a state-changing call
Every state-changing chat call—adding or rotating conversation keys, creating a group, adding members—requiresaction_signatures in the request body, validated at the API boundary. A missing or malformed entry (each needs message_id, encoded_message_event_detail, and a message_event_signature with signature, public_key_version, and signature_version) returns an HTTP 400 problem-details response immediately. Use the SDK prepare methods (prepare_conversation_key_change, prepare_group_create, prepare_group_members_change) and send all returned signatures—group create and member adds return two.
Media encrypt and decrypt
- Use the same conversation key (and version) as the message that references the attachment
- Treat download responses as ciphertext until you run
decrypt_stream - Infer MIME type after decrypt; the download
Content-Typeis often not the real image type
Safe debugging
When investigating crypto failures:- Log conversation ids, event ids, and key versions only
- Do not log plaintext, PINs, private keys, or full key blobs
- Confirm
set_key_versionmatches thepublic_key_versionon your public-key record - For incomplete history, page all event pages so key-change metadata is not skipped before decrypting