Skip to main content
This page covers problems that are specific to X Chat encryption and the Chat XDK—keys, Juicebox, decrypt/verify, and building encrypted send payloads. For webhooks, OAuth, HTTP status codes, and rate limits, use the general X API and authentication documentation.

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

Encrypt or decrypt fails because keys are not loaded

Load private keys first, then set the public-key version from your record on X.

Missing conversation key for a message

You do not have the raw key for that message’s conversation_key_version.
  1. Decrypt key material from conversation_key_change_event (live events) or meta.conversation_key_events (history) with extract_conversation_keys, or include those blobs in decrypt_events
  2. 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, load public_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_event string
  • 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
The set_reject_unverified setter exists to opt out of this default (false, not recommended). If you disabled it earlier, restore the fail-closed default:

Old events permanently fail verification

Errors like signature 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—requires action_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-Type is often not the real image type
Details: Media.

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_version matches the public_key_version on your public-key record
  • For incomplete history, page all event pages so key-change metadata is not skipped before decrypting