stablyai/orca · critical · MobileE2EEAuthenticationError
E2EE device authentication rejected
Error message
E2EE device authentication rejected
What it means
`MobileE2EEAuthenticationError` is thrown by the physical channel when, in the `awaiting-authenticated` state, the decrypted plaintext is a JSON message with `type === 'e2ee_error'` (detected by `isAuthenticationRejection`). That is the desktop's explicit signal that the mobile device's `e2ee_auth` payload — device token or transcript hash — was rejected. It is a distinct, named error class so callers can branch on it rather than string-matching.
Source
Thrown at mobile/src/transport/mobile-e2ee-v2-physical-channel.ts:108
private async processMessage(raw: unknown, generation: number): Promise<void> {
if (generation !== this.generation) {
return
}
if (this.state === 'awaiting-ready') {
this.acceptReady(raw)
return
}
const plaintext =
typeof raw === 'string'
? this.args.session.openText(raw)
: await this.openBinary(raw, generation)
if (generation !== this.generation || plaintext === null) {
return
}
if (this.state === 'awaiting-authenticated') {
if (typeof plaintext === 'string' && isAuthenticationRejection(plaintext)) {
throw new MobileE2EEAuthenticationError()
}
if (typeof plaintext !== 'string' || !this.isAuthenticated(plaintext)) {
throw new Error('Invalid E2EE v2 authenticated response')
}
this.state = 'ready'
this.args.onAuthenticated()
} else if (typeof plaintext === 'string') {
this.args.onText(plaintext)
} else {
this.args.onBinary(plaintext)
}
}
private acceptReady(raw: unknown): void {
if (typeof raw !== 'string') {
throw new Error('Expected plaintext E2EE v2 ready')
}
let ready: unknownView on GitHub (pinned to 1136503c6a)
Solutions
- Catch `MobileE2EEAuthenticationError` specifically in the transport error handler and surface a re-pair flow rather than retrying.
- If the device token may be stale, clear the stored credential bundle and re-run pairing.
- Verify the desktop is running a protocol-compatible version (v2 e2ee) before retry.
Example fix
// before
try { link.sendText('hello') } catch (e) { retry() } // wrong: auth errors are not retried
// after
try {
// ...
} catch (e) {
if (e instanceof MobileE2EEAuthenticationError) {
await clearDeviceTokenAndStartRepair(hostId)
} else {
throw e
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// No pre-call validation: the rejection is the desktop's response. Detect after the fact. // Ensure onError handler branches on the error class.
Type guard
function isAuthRejection(e: unknown): e is MobileE2EEAuthenticationError { return e instanceof MobileE2EEAuthenticationError } Try / catch
channel.onError = (e) => { if (e instanceof MobileE2EEAuthenticationError) { startRepairFlow(hostId) } else { reportTransient(e) } } Prevention
- Always check `instanceof MobileE2EEAuthenticationError` in the error handler.
- On this error, clear the device token and re-pair rather than retrying.
- Keep device tokens fresh — detect server-side revocation via resume failures.
When it happens
Trigger: Desktop replied to `e2ee_auth` with `{ "type": "e2ee_error", ... }`; the device token presented in `e2ee_auth` is unknown, expired, or revoked on the desktop side; the transcript hash did not match the desktop's view of the handshake transcript.
Common situations: A host that was unpaired or reset while the mobile still held a stale device token; desktop-side rollback to a state that does not recognize the device; user revoked the device from the desktop UI between pairing and reconnect.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- relay_outer_${code}
- relay credential resolved as an unexpected credential kind
- Invalid E2EE v2 authenticated response
- Invalid E2EE v2 ready
- invalid relay hello JSON
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/1f611b18793b465b.
Report an issue: GitHub.