Files
wz-phone/crates/wzp-proto/src/error.rs
Siavash Sameni da08723fe7 fix(signal): forward-compat — log+continue on unknown SignalMessage variants
Both sides of the signal channel previously broke their recv loop
on any deserialize error, which meant adding a new variant in one
build silently killed signal connections from peers running an
older build. This bit us during Phase 1 testing: a new client
sending SignalMessage::Reflect to a pre-Phase-1 relay caused the
relay to drop the whole signal connection, which looked like
"Error: not registered" on the next place_call.

Fix:
- New TransportError::Deserialize(String) variant in wzp-proto
  carries serde errors as a distinct category.
- wzp-transport/reliable.rs::recv_signal returns Deserialize on
  serde_json::from_slice failures (was wrapped in Internal).
- wzp-relay/main.rs signal loop matches on Deserialize → warn +
  continue (instead of break).
- desktop/src-tauri/lib.rs recv loop does the same.

Other TransportError variants (ConnectionLost, Io, Internal) still
break the loop — only pure parse failures are recoverable.

This means future SignalMessage variant additions are backward-
compat by construction: older peers will see "unknown variant,
continuing" in their logs while newer peers can keep evolving the
protocol.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 18:13:31 +04:00

77 lines
2.4 KiB
Rust

use thiserror::Error;
/// Errors from audio codec operations.
#[derive(Debug, Error)]
pub enum CodecError {
#[error("encode failed: {0}")]
EncodeFailed(String),
#[error("decode failed: {0}")]
DecodeFailed(String),
#[error("unsupported profile transition from {from:?} to {to:?}")]
UnsupportedTransition {
from: crate::CodecId,
to: crate::CodecId,
},
}
/// Errors from FEC operations.
#[derive(Debug, Error)]
pub enum FecError {
#[error("source block is full (max {max} symbols)")]
BlockFull { max: usize },
#[error("decode impossible: need {needed} symbols, have {have}")]
InsufficientSymbols { needed: usize, have: usize },
#[error("invalid block id {0}")]
InvalidBlock(u8),
#[error("internal FEC error: {0}")]
Internal(String),
}
/// Errors from cryptographic operations.
#[derive(Debug, Error)]
pub enum CryptoError {
#[error("decryption failed (bad key or tampered data)")]
DecryptionFailed,
#[error("invalid public key")]
InvalidPublicKey,
#[error("rekey failed: {0}")]
RekeyFailed(String),
#[error("anti-replay: duplicate or old packet (seq={seq})")]
ReplayDetected { seq: u16 },
#[error("internal crypto error: {0}")]
Internal(String),
}
/// Errors from transport operations.
#[derive(Debug, Error)]
pub enum TransportError {
#[error("connection lost")]
ConnectionLost,
#[error("datagram too large: {size} bytes (max {max})")]
DatagramTooLarge { size: usize, max: usize },
#[error("connection timeout after {ms}ms")]
Timeout { ms: u64 },
#[error("io error: {0}")]
Io(#[from] std::io::Error),
/// Parsed wire bytes successfully but the payload didn't
/// deserialize into a known `SignalMessage` variant. Usually
/// means the peer is running a newer build with a variant we
/// don't know yet. Callers should **log and continue** rather
/// than tearing down the connection, so that forward-compat
/// additions to `SignalMessage` don't silently kill old
/// clients/relays.
#[error("signal deserialize: {0}")]
Deserialize(String),
#[error("internal transport error: {0}")]
Internal(String),
}
/// Errors from obfuscation layer.
#[derive(Debug, Error)]
pub enum ObfuscationError {
#[error("obfuscation failed: {0}")]
Failed(String),
#[error("deobfuscation failed: invalid framing")]
InvalidFraming,
}