Summary
extract_conversation_keys() returns a valid conversation-key map, but decrypt_events() does not see those keys — every event fails with Available versions: [], naming the exact version that extract_conversation_keys() just returned. set_cache_keys(true) is enabled and makes no difference.
Passing the same map to decrypt_event() explicitly works, so the key material and the ciphertext are both fine; only the cache hand-off between the two calls is broken.
Reproduction
Python (chatxdk), against a live group conversation:
engine.set_cache_keys(True)
engine.set_signing_keys(signing_keys)
extracted = engine.extract_conversation_keys(key_change_events)
# -> {'keys': {'1785442942475': b'...'}, 'latest_version': '1785442942475'}
result = engine.decrypt_events(message_events)
# -> messages: 0, errors: 10
# "Crypto error: Decryption failed: Message encrypted with key version
# '1785442942475' but no matching key found. Available versions: [].
# Call extract_conversation_keys() first."
engine.decrypt_event(message_events[0], extracted["keys"])
# -> works, returns the Message
The error text advises calling extract_conversation_keys() first, which is exactly what was done immediately before, so the message sends you in a circle.
Impact
decrypt_events() is the documented batch path (Getting Started, step 6: "Use decrypt_events() for batch processing"). As written it returns zero messages for a normal conversation, and the error blames a missing key rather than the cache, so it reads like a key-provisioning problem on the caller's side. Working around it means abandoning the batch API and looping decrypt_event() per event with the map threaded through manually.
Related doc gap
The key-change events that feed extract_conversation_keys() are not in the events response's data array — they arrive in meta.conversation_key_events. That is easy to miss, and getting it wrong produces the identical Available versions: [] error, which makes the two problems hard to tell apart. Worth calling out explicitly in the Getting Started decrypt section.
Environment
chatxdk (Python), 2026-07-30
xdk 0.6.6
- Python 3.12, macOS
- Auth: OAuth 2.0 user context,
dm.read / dm.write
- Identity loaded via
import_keys() from an exported key blob; is_unlocked() true, set_reject_unverified(True)
Summary
extract_conversation_keys()returns a valid conversation-key map, butdecrypt_events()does not see those keys — every event fails withAvailable versions: [], naming the exact version thatextract_conversation_keys()just returned.set_cache_keys(true)is enabled and makes no difference.Passing the same map to
decrypt_event()explicitly works, so the key material and the ciphertext are both fine; only the cache hand-off between the two calls is broken.Reproduction
Python (
chatxdk), against a live group conversation:The error text advises calling
extract_conversation_keys()first, which is exactly what was done immediately before, so the message sends you in a circle.Impact
decrypt_events()is the documented batch path (Getting Started, step 6: "Usedecrypt_events()for batch processing"). As written it returns zero messages for a normal conversation, and the error blames a missing key rather than the cache, so it reads like a key-provisioning problem on the caller's side. Working around it means abandoning the batch API and loopingdecrypt_event()per event with the map threaded through manually.Related doc gap
The key-change events that feed
extract_conversation_keys()are not in the events response'sdataarray — they arrive inmeta.conversation_key_events. That is easy to miss, and getting it wrong produces the identicalAvailable versions: []error, which makes the two problems hard to tell apart. Worth calling out explicitly in the Getting Started decrypt section.Environment
chatxdk(Python), 2026-07-30xdk0.6.6dm.read/dm.writeimport_keys()from an exported key blob;is_unlocked()true,set_reject_unverified(True)