Skip to content

Broaden MP cache-invalidate to all messageprotection-* codes#28

Open
dav12072018 wants to merge 1 commit into
OpenBubbles:masterfrom
dav12072018:broaden-mp-cache-invalidate
Open

Broaden MP cache-invalidate to all messageprotection-* codes#28
dav12072018 wants to merge 1 commit into
OpenBubbles:masterfrom
dav12072018:broaden-mp-cache-invalidate

Conversation

@dav12072018
Copy link
Copy Markdown

What this changes

One-line behavioral change in src/imessage/aps_client.rs (command:120 error handling). Previously only ec-com.apple.messageprotection-802 invalidated the per-pair identity cache; this PR broadens that to any ec-com.apple.messageprotection-* code.

- if error_string == "ec-com.apple.messageprotection-802" {
+ if error_string.starts_with("ec-com.apple.messageprotection-") {
      // refreshing identity cache can fix this
      let mut cache_lock = self.identity.cache.lock().await;
      cache_lock.invalidate(&target, &sender);
  }

Why

Filed as OpenBubbles/openbubbles-app#206 (full investigation and log evidence there). Short version:

messageprotection-6 (signature/decrypt verification failed) and -802 (NGM count drift) are both downstream symptoms of the same root condition — the per-device session key in rustpush's identity cache is stale relative to what the recipient device actually has. The recovery path (invalidate the cached pair, force a fresh IDS lookup on next send) works for both.

Without this fix, MP-6 errors propagate to the Dart UI as Message::Error and are rendered as send failures, even when Apple's IDS already accepted and delivered the message to the chat recipient. This is especially visible for self-loop errors (sP == tP) — the user's own other devices rejecting the reflected copy — where every retry hits the same stale cached key and produces the same MP-6, so the user sees a permanent "failed to send" state on messages that actually delivered.

Evidence from one user's log over 9 hours:

  • 81 MP-6 errors
  • 100% self-loops (sP == tP)
  • 23 distinct failing device-tokens, concentrated on ~5 stuck devices
  • fR: 200 on all of them — Apple acked delivery; the per-recipient decryption failed

Tested by

⚠️ Not tested locally. I don't have a working OpenBubbles/iMessage setup to validate end-to-end. The change is small enough to reason about statically, but please review with that caveat in mind.

In particular, the assumption being made is that every ec-com.apple.messageprotection-* code is recoverable via cache invalidation. If there are MP codes that should not invalidate the cache (e.g. ones indicating the recipient is permanently gone, or where invalidation would trigger a tight retry loop), this would need a more targeted match — for example an allowlist of known recoverable codes, or excluding specific ones.

I marked this draft for that reason. Happy to narrow the match (e.g. just -6 + -802) if starts_with is too broad.

Alternative considered

A more semantically correct fix would be in process_msg: detect self-loop errors (sP == this client's own handle) and suppress them from being surfaced as Message::Error to the Dart layer at all, since the chat recipient already got the message. That would be a larger change and I wasn't sure of the right way to compare against the client's own registered handles in this code — happy to attempt that direction instead if preferred.

Refs: OpenBubbles/openbubbles-app#206

Previously only "ec-com.apple.messageprotection-802" triggered identity
cache invalidation on a command:120 error. The same recovery path
(invalidating the cached per-device session keys for the failing pair)
also resolves "-6" (signature/decrypt verification failed on the
recipient device) and other recoverable message-protection codes that
indicate a stale per-device session key.

Without this, MP-6 errors propagate to the UI as send failures even when
Apple's IDS already accepted and delivered the message to the chat
recipient. This is particularly visible for self-loop errors (sP == tP)
where the user's own other devices reject the reflected copy: the chat
recipient sees the message, but the sender's OpenBubbles UI marks it
failed and never recovers because the stale cached session keeps
producing the same failure on retry.

Refs: openbubbles-app#206
@dav12072018 dav12072018 marked this pull request as ready for review May 16, 2026 17:04
@TaeHagen
Copy link
Copy Markdown
Collaborator

Hi,

Your PR appears to be written by AI - can you please provide your source for the meaning of errors -6 and -802?

Thank you.

@dav12072018
Copy link
Copy Markdown
Author

dav12072018 commented May 17, 2026

Hi,

Your PR appears to be written by AI - can you please provide your source for the meaning of errors -6 and -802?

Thank you.

What I have for -802: the existing comment in your own code (// refreshing identity cache can fix this) plus the fact that you already special-case it. That's not a source for what -802 means, just confirmation that you've already treated it as a recoverable cache-invalidation case.

What I have for -6: I don't have a documented Apple source. Apple's iMessage protection error codes aren't publicly documented anywhere I could find. My reasoning was inferential, from log evidence in one user's rustpush trace:

81 MP-6 errors in 9 hours, 100% self-loops (sP == tP), all with fR: 200 (Apple's relay accepted the send)
23 distinct failing fU device-tokens, with a long tail concentrated on ~5 specific tokens that kept failing across many different message UUIDs
The chat recipient did receive every message — only the sender's own reflected copies were rejected
Re-registering OpenBubbles (multiple times in the same log) didn't help, but clearing the identity cache empirically does per the #170 thread
Same failing device-tokens persisted across message UUIDs, which fits "stale per-device session key" better than "permanent rejection" or "rate limit"
That's a symptom pattern consistent with stale NGM session state on a per-device basis, which is the same class of condition -802 recovery already handles. But you're right — I called it "signature/decrypt verification failed" with more confidence than the evidence supports. I shouldn't have. The honest statement is "behaves like a recoverable stale-key condition in the logs I have," not "means X per Apple."

See this for the full investigation. OpenBubbles/openbubbles-app#206

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants