{"record":{"id":"e54fa44235619e97","repo":"discordjs/discord.js","slug":"failed-to-parse-packet","errorCode":null,"errorMessage":"Failed to parse packet","messagePattern":"Failed to parse packet","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/voice/src/receive/VoiceReceiver.ts","lineNumber":166,"sourceCode":"\t\tsecretKey: Uint8Array,\n\t\tuserId: string,\n\t\tssrc: number,\n\t): AudioPacket | null {\n\t\t// Parse key RTP Header fields\n\t\tconst first = rtp.readUint8();\n\t\tconst hasHeaderExtension = Boolean((first >> 4) & 0x01); // X field\n\t\tconst cc = first & 0x0f; // CSRC Count field\n\t\tconst sequence = rtp.readUInt16BE(2);\n\t\tconst timestamp = rtp.readUInt32BE(4);\n\n\t\t// Compute unencrypted header size: fixed header + CSRC Identifiers + extension header if present\n\t\tlet headerSize = 12 + 4 * cc;\n\t\tconst extensionHeaderOffset = headerSize; // where the extension header starts, if present\n\t\tif (hasHeaderExtension) headerSize += 4; // extension header (profile ID + length)\n\n\t\t// Decrypt the RTP Payload\n\t\tlet payload: Buffer = this.decrypt(rtp, mode, nonce, secretKey, headerSize);\n\t\tif (!payload) throw new Error('Failed to parse packet');\n\n\t\t// Strip padding (RFC3550 5.1)\n\t\tconst hasPadding = rtp[0] && Boolean(rtp[0] & 0b100000);\n\t\tif (hasPadding) {\n\t\t\tconst paddingAmount = payload[payload.length - 1]!;\n\t\t\tif (paddingAmount < payload.length) {\n\t\t\t\tpayload = payload.subarray(0, payload.length - paddingAmount);\n\t\t\t}\n\t\t}\n\n\t\t// Skip the decrypted RTP Header Extension data if present\n\t\tif (hasHeaderExtension) {\n\t\t\t// Extension Header Length field\n\t\t\tconst headerExtensionLength = rtp.readUInt16BE(extensionHeaderOffset + 2);\n\t\t\tpayload = payload.subarray(4 * headerExtensionLength);\n\t\t}\n\n\t\t// Decrypt payload if in a DAVE session.","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/discordjs/discord.js/blob/a81ed8a306d37fdc746e26a634b6a42164ba2c8c/packages/voice/src/receive/VoiceReceiver.ts#L148-L184","documentation":"After decrypting an RTP packet, parsePacket checks the decryption result; if decrypt returns a falsy value (empty/failed decrypt) it throws this generic Error. A null/empty payload means decryption produced nothing usable — usually a wrong nonce construction, wrong secret key, or a corrupted/short packet — so the packet cannot be yielded as an Opus frame.","triggerScenarios":"Receiving an RTP packet that fails to decrypt to a non-empty buffer: packets arriving with a mismatched nonce padding for the negotiated mode, key/mode mismatch between the connection and receiver, or malformed/truncated datagrams (e.g. headerSize >= datagram length).","commonSituations":"Flaky networks dropping/corrupting UDP packets; receiving packets from a different voice session after reconnect (stale secret key); older Discord server behavior with nonce suffix modes; non-audio datagrams hitting the socket.","solutions":["Wrap parsePacket/subscribe handlers so corrupt packets are skipped — individual packet loss is expected in RTP and safe to ignore.","Rejoin the voice channel / recreate the receiver after a reconnect so a fresh secretKey is used.","Update @discordjs/voice to the latest version to get nonce-handling fixes for your negotiated mode.","Check network stability (packet loss, VPN/firewall interference) if a large fraction of packets fail."],"exampleFix":"// before\nreceiver.subscribe(userId); // throws on corrupt packet, killing the pipeline\n// after\nconst sub = receiver.subscribe(userId);\nsub.on('error', (e) => {\n  if (e.message === 'Failed to parse packet') return; // skip corrupt packet\n  throw e;\n});","handlingStrategy":"try-catch","validationCode":"// Sanity-check the RTP datagram before parsing:\nfunction isPlausibleRtp(buf: Buffer): boolean {\n  return buf.length >= 12 && (buf[0]! >> 6) === 2; // version 2, min header length\n}","typeGuard":"function isRtpPacket(buf: Buffer): boolean {\n  return Buffer.isBuffer(buf) && buf.length >= 12 && (buf[0]! & 0xc0) === 0x80;\n}","tryCatchPattern":"try {\n  const opus = receiver.parsePacket(rtp, mode, nonce, secretKey);\n} catch (e) {\n  if (e.message === 'Failed to parse packet') return; // drop corrupt RTP packet, continue stream\n  throw e;\n}","preventionTips":["Treat individual packet failures as normal RTP loss — never crash the pipeline for one packet.","Refresh secretKey/mode on every voice reconnect to avoid decrypting with stale keys.","Keep @discordjs/voice current for nonce-handling fixes.","Improve network reliability (wired connection, no UDP-intercepting proxies) to reduce corrupt packets."],"tags":["rtp","udp","decryption","packet-parsing"],"backgroundTag":"packet-decryption-failed","analyzedSha":"a81ed8a306d37fdc746e26a634b6a42164ba2c8c","analyzedAt":"2026-08-30T04:07:22.193Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}