BloopAI/vibe-kanban · error
invalid relay WS sequence: expected {expected_seq}, got {}
Error message
invalid relay WS sequence: expected {expected_seq}, got {} What it means
Signed WS envelopes carry a monotonically increasing sequence number; decode() requires the inbound envelope's seq to equal inbound_seq + 1 to detect replay, drops, or reordering under the negotiated crypto session. Any other sequence is rejected.
Source
Thrown at crates/relay-ws/src/crypto.rs:95
}
}
/// Verify a signed JSON envelope and deserialize it back into a frame.
///
/// Checks the Ed25519 signature and enforces monotonic sequence ordering.
pub(crate) fn decode(&mut self, raw: &[u8]) -> anyhow::Result<RelayWsFrame> {
use anyhow::Context as _;
let envelope: SignedWsEnvelope =
serde_json::from_slice(raw).context("invalid relay WS envelope JSON")?;
if envelope.version != ENVELOPE_VERSION {
anyhow::bail!("unsupported relay WS envelope version");
}
let expected_seq = self.inbound_seq.saturating_add(1);
if envelope.seq != expected_seq {
anyhow::bail!(
"invalid relay WS sequence: expected {expected_seq}, got {}",
envelope.seq
);
}
let payload = BASE64_STANDARD
.decode(&envelope.payload_b64)
.context("invalid relay WS payload")?;
let signing_input = ws_signing_input(
&self.request_signature,
envelope.seq,
envelope.msg_type,
&payload,
);
let signature_bytes = BASE64_STANDARD
.decode(&envelope.signature_b64)
.context("invalid relay WS frame signature encoding")?;View on GitHub (pinned to 4deb7eca8f)
Solutions
- Ensure envelopes are delivered in order and exactly once over the session
- Re-establish/re-key the crypto session after reconnects so both seq counters reset
- Drop duplicates before calling decode(); check envelope.seq vs inbound_seq before processing
- Debug which seq was received vs expected in the log message
Example fix
// before
let _ = crypto.decode(envelope_seq_5)?; // after already decoding seq 4, expected 5... got 5 fine; got 7 -> bail
let _ = crypto.decode(envelope_seq_7)?;
// after
let seq = envelope.seq;
if seq != crypto.expected_inbound_seq() { crypto.reset_session()?; }
let _ = crypto.decode(envelope)?; Defensive patterns
Strategy: retry
Validate before calling
if envelope.seq != crypto.expected_inbound_seq() { // buffer or request retransmit before decode }
Try / catch
match crypto.decode(raw) { Err(e) if e.to_string().starts_with("invalid relay WS sequence") => resync_or_rekey_session(), other => other? } Prevention
- Guarantee ordered delivery (use a single WS stream, no fan-out reordering)
- Reset both seq counters on every reconnect/re-key
- Drop duplicate envelopes before decode
- Monitor sequence-gap logs to detect transport loss
When it happens
Trigger: Second call to decode() on a fresh CryptoSession reusing an old first envelope; out-of-order WS message delivery; duplicated envelope delivery; a peer restarting and resetting its seq counter while this side retains state.
Common situations: Message replay/reordering across flaky transports; reconnect logic replaying buffered envelopes; mismatched session pairing where two encoders feed one decoder.
Related errors
- unsupported relay WS envelope version
- Server message has invalid length.
- Server message has invalid side identifier.
- Raw stream should only have Stdout/Stderr/Finished
- invalid close payload
AI-assisted analysis of BloopAI/vibe-kanban@4deb7eca8f (2026-08-29).
Data as JSON: /api/errors/4a0806ea22b7b938.
Report an issue: GitHub.