agalwood/Motrix · error
IV must be 16 bytes, got ${iv.length}
Error message
IV must be 16 bytes, got ${iv.length} What it means
Plain Error thrown by SegmentDecryptor.decrypt when the iv argument is not exactly 16 bytes. AES-128-CBC IVs are exactly one block (128 bits = 16 bytes); a different length means the caller built the IV wrong (e.g. used a 32-bit value, an 8-byte uint64, or a hex string).
Source
Thrown at src/core/media/segment-decryptor.ts:32
this.defaultFetchKey = async (uri: string) => {
const response = await fetch(uri)
const buffer = await response.arrayBuffer()
const key = new Uint8Array(buffer)
if (key.length !== 16) {
throw new Error(`Key must be 16 bytes, got ${key.length} from ${uri}`)
}
return key
}
}
}
decrypt(ciphertext: Uint8Array, key: Uint8Array, iv: Uint8Array): Uint8Array {
// Validate key and IV lengths
if (key.length !== 16) {
throw new Error(`Key must be 16 bytes, got ${key.length}`)
}
if (iv.length !== 16) {
throw new Error(`IV must be 16 bytes, got ${iv.length}`)
}
// Convert Uint8Array to Buffer for crypto operations
const keyBuffer = Buffer.from(key)
const ivBuffer = Buffer.from(iv)
const ciphertextBuffer = Buffer.from(ciphertext)
// Try with PKCS7 auto-padding first
try {
const decipher = createDecipheriv('aes-128-cbc', keyBuffer, ivBuffer)
const plaintext = Buffer.concat([
decipher.update(ciphertextBuffer),
decipher.final(),
])
return new Uint8Array(plaintext)
} catch (err) {
// Only fall back if error is PKCS7-padding related
const msg = err instanceof Error ? err.message.toLowerCase() : ''View on GitHub (pinned to 1a708ee577)
Solutions
- Use the provided seqNumberIv(seq) helper to build sequence-number IVs — it always returns 16 bytes.
- If you have an explicit IV hex string, decode it: Buffer.from(ivHex.replace(/^0x/, '').padStart(32, '0'), 'hex').
- At the call site assert iv.length === 16 for a clearer error before decrypt.
- Do not reuse AES-GCM 12-byte nonces for AES-128-CBC — they are different cipher IV shapes.
Example fix
// before — passing an 8-byte bigint IV
decryptor.decrypt(seg, key, Buffer.from([...bigIntToBytes(seq, 8)]))
// after — use the helper that produces a 16-byte IV
import { seqNumberIv } from './segment-plan'
decryptor.decrypt(seg, key, seqNumberIv(seq)) Defensive patterns
Strategy: validation
Validate before calling
function isAes128Iv(iv: Uint8Array): boolean {
return iv.length === 16
}
if (!isAes128Iv(iv)) {
throw new Error(`iv must be 16 bytes; got ${iv.length}`)
}
decryptor.decrypt(ciphertext, key, iv) Type guard
function isAes128Iv(iv: Uint8Array): boolean {
return iv instanceof Uint8Array && iv.length === 16
} Try / catch
try {
decryptor.decrypt(ciphertext, key, iv)
} catch (e) {
if (/IV must be 16 bytes/.test(String(e))) {
// rebuild IV via seqNumberIv(seq) or decode hex
} else throw e
} Prevention
- Use seqNumberIv(seq) for sequence-based IVs — it always returns 16 bytes.
- Decode hex IV strings with the parseIvHex helper or Buffer.from(hex, 'hex') before passing.
- Do not conflate AES-GCM 12-byte nonces with AES-CBC 16-byte IVs.
When it happens
Trigger: Calling decryptor.decrypt(ciphertext, key, iv) where iv.length !== 16. Typical when the caller passed a 4-byte sequence number, an 8-byte Buffer from BigInt, or a 32-char hex string instead of the decoded 16-byte form.
Common situations: Caller used seqNumberIv correctly elsewhere but built a custom IV wrong here; passed the IV as a hex string; confused the IV with a shorter nonce from another cipher (AES-GCM uses 12-byte nonces); off-by-one subarray slice.
Related errors
- Key must be 16 bytes, got ${key.length}
- unsupported-encryption
- Key must be 16 bytes, got ${key.length} from ${uri}
- IpcInvalidPayload
- taskId must be a string
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/daa9fcba619ca1fb.
Report an issue: GitHub.