{"record":{"id":"43850a91539e5953","repo":"n0-computer/iroh","slug":"emptypacket-43850a","errorCode":"EmptyPacket","errorMessage":"Attempted to send empty packet","messagePattern":"Attempted to send empty packet","errorType":"validation","errorClass":"SendError","httpStatus":null,"severity":"error","filePath":"iroh-relay/src/server/streams.rs","lineNumber":137,"sourceCode":"\nimpl<S> Sink<RelayToClientMsg> for RelayedStream<S>\nwhere\n    S: Sink<bytes::Bytes, Error = StreamError> + Unpin,\n{\n    type Error = SendError;\n\n    fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        Pin::new(&mut self.inner).poll_ready(cx).map_err(Into::into)\n    }\n\n    fn start_send(mut self: Pin<&mut Self>, item: RelayToClientMsg) -> Result<(), Self::Error> {\n        let size = item.encoded_len();\n        ensure!(\n            size <= MAX_PACKET_SIZE,\n            SendError::ExceedsMaxPacketSize { size }\n        );\n        if let RelayToClientMsg::Datagrams { datagrams, .. } = &item {\n            ensure!(!datagrams.contents.is_empty(), SendError::EmptyPacket);\n        }\n\n        Pin::new(&mut self.inner)\n            .start_send(item.to_bytes().freeze())\n            .map_err(Into::into)\n    }\n\n    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        Pin::new(&mut self.inner).poll_flush(cx).map_err(Into::into)\n    }\n\n    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n        Pin::new(&mut self.inner).poll_close(cx).map_err(Into::into)\n    }\n}\n\n/// Relay receive errors\n#[stack_error(derive, add_meta, from_sources)]","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/n0-computer/iroh/blob/2b4de030ce5e0133f272871a76f0c685c63f552a/iroh-relay/src/server/streams.rs#L119-L155","documentation":"The relay server attempted to send a RelayToClientMsg::Datagrams packet whose datagram list is empty. Empty packets are meaningless on the relay stream, so start_send rejects them with SendError::EmptyPacket before serialization.","triggerScenarios":"Calling start_send with a RelayToClientMsg::Datagrams variant where datagrams.contents is empty — typically produced by code that drains a queue and builds a datagram packet without checking it collected anything.","commonSituations":"Flush loops that build a Datagrams message each tick regardless of pending items, race between queue check and packet construction, refactored code paths that removed the emptiness check upstream.","solutions":["Check datagrams.contents.is_empty() before constructing/enqueueing the Datagrams message and skip sending if empty.","Only build Datagrams packets after confirming at least one datagram is pending.","If an empty message represents a keep-alive, use the relay's dedicated ping/pong message type instead."],"exampleFix":"// before\nlet msg = RelayToClientMsg::Datagrams { datagrams };\nsink.start_send(msg)?;\n// after\nif !datagrams.contents.is_empty() {\n    sink.start_send(RelayToClientMsg::Datagrams { datagrams })?;\n}","handlingStrategy":"validation","validationCode":"fn is_sendable(msg: &RelayToClientMsg) -> bool {\n    match msg { RelayToClientMsg::Datagrams { datagrams, .. } => !datagrams.contents.is_empty(), _ => true }\n}","typeGuard":"fn non_empty_datagrams(d: Datagrams) -> Option<Datagrams> { (!d.contents.is_empty()).then_some(d) }","tryCatchPattern":"match sink.start_send(msg) {\n    Err(SendError::EmptyPacket) => { /* skip: nothing to send */ }\n    other => other?,\n}","preventionTips":["Skip building Datagrams packets when the pending queue is empty.","Assert queue non-emptiness at packet-construction sites.","Use the relay ping message for keep-alives, not empty datagram packets.","Review refactors that removed emptiness checks."],"tags":["relay","empty-packet","validation","network"],"backgroundTag":"empty-required-field","analyzedSha":"2b4de030ce5e0133f272871a76f0c685c63f552a","analyzedAt":"2026-09-08T04:26:47.755Z","contentChangedAt":"2026-09-08T04:26:47.755Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}