{"record":{"id":"66925e142d27e81b","repo":"slackhq/nebula","slug":"writebatch-len-bufs-d-len-addrs-d","errorCode":null,"errorMessage":"WriteBatch: len(bufs)=%d != len(addrs)=%d","messagePattern":"WriteBatch: len\\(bufs\\)=(.+?) != len\\(addrs\\)=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"udp/udp_linux_writebatch.go","lineNumber":196,"sourceCode":"\tminor, _ = strconv.Atoi(mp)\n\treturn\n}\n\n// WriteBatch sends bufs via sendmmsg(2), coalescing runs into UDP_SEGMENT\n// entries, so one syscall can mix GSO superpackets and plain datagrams.\n// Without GSO support every packet is its own entry.\n// Callers shall deliver same-destination packets contiguously and in counter order\n//\n// Batches larger than the scratch take one sendmmsg per chunk.\n// A partial success resumes the same prepared entries at the first unsent entry.\n// A zero-sent error means the kernel rejected the first remaining entry:\n// its packets are dropped and the rest of the chunk resumes in place.\n//\n// Returns the number of packets sent. An error means the call itself failed.\n// A short count means some destinations were undeliverable.\nfunc (w *batchWriter) WriteBatch(bufs [][]byte, addrs []netip.AddrPort) (int, error) {\n\tif len(bufs) != len(addrs) {\n\t\treturn 0, fmt.Errorf(\"WriteBatch: len(bufs)=%d != len(addrs)=%d\", len(bufs), len(addrs))\n\t}\n\n\t// A destination the kernel rejects results in us dropping that entry (one packet, or one same-destination GSO run).\n\t// We count what actually made it out rather than returning an error.\n\twritten := 0\n\n\ti := 0\n\tfor i < len(bufs) {\n\t\tentry := 0\n\t\tiovIdx := 0\n\t\tfor entry < len(w.msgs) && i < len(bufs) {\n\t\t\tiovBudget := len(w.iovs) - iovIdx\n\t\t\tif iovBudget < 1 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\trunLen, segSize := w.planRun(bufs, addrs, i, iovBudget)\n\t\t\tif runLen == 0 {\n\t\t\t\tbreak","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/slackhq/nebula/blob/dd8f660c0ac37903ec4080ca4d3c861ba9342ceb/udp/udp_linux_writebatch.go#L178-L214","documentation":"batchWriter.WriteBatch requires the bufs (payloads) and addrs (destinations) slices to have the same length, because each buffer maps to exactly one sendmmsg destination. The library rejects mismatched inputs up front rather than silently dropping packets.","triggerScenarios":"Calling WriteBatch (via StdConn.WriteTo/udpsend batching path) with len(bufs) != len(addrs), typically from a custom caller or a bug in caller-side batching that appends to one slice but not the other.","commonSituations":"Custom integrations driving the udp package directly; off-by-one when trimming failed entries from addrs but not bufs; refactoring the emit loop in nebula's interface code.","solutions":["Fix the caller so every buffer has exactly one corresponding destination address","If intentionally dropping packets, slice both bufs and addrs to the same length before calling","Add a debug assertion/log in the caller when the slices diverge to catch the divergence early"],"exampleFix":"// before\nw.WriteBatch(bufs[:n], addrs[:m]) // n != m\n// after\nn := min(n, m)\nw.WriteBatch(bufs[:n], addrs[:n])","handlingStrategy":"validation","validationCode":"func safeWriteBatch(w *udp.StdConn, bufs [][]byte, addrs []netip.AddrPort) (int, error) {\n    n := min(len(bufs), len(addrs))\n    return w.WriteBatch(bufs[:n], addrs[:n])\n}","typeGuard":null,"tryCatchPattern":"n, err := w.WriteBatch(bufs, addrs)\nif err != nil {\n    if strings.Contains(err.Error(), \"len(bufs)\") {\n        log.Error(\"caller bug: bufs/addrs length mismatch\", \"bufs\", len(bufs), \"addrs\", len(addrs))\n    }\n    return n, err\n}","preventionTips":["Always append to bufs and addrs in the same loop iteration","Trim both slices together when dropping failed entries","Add a debug assert in caller code comparing len(bufs)==len(addrs)","Write a unit test covering the batching emit path"],"tags":["udp","linux","writebatch","programming-error","argument-validation"],"backgroundTag":"slice-length-mismatch","analyzedSha":"dd8f660c0ac37903ec4080ca4d3c861ba9342ceb","analyzedAt":"2026-09-03T11:13:55.444Z","contentChangedAt":"2026-09-03T11:13:55.444Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}