agalwood/Motrix · error
Key must be 16 bytes, got ${key.length}
Error message
Key must be 16 bytes, got ${key.length} What it means
Plain Error thrown by SegmentDecryptor.decrypt when the key argument is not exactly 16 bytes. This guards the public decrypt API (not the fetcher) against callers passing a wrong-size key buffer — a programming error rather than a network one, since decrypt has no URI context to report.
Source
Thrown at src/core/media/segment-decryptor.ts:29
if (fetchKey) {
this.defaultFetchKey = fetchKey
} else {
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)View on GitHub (pinned to 1a708ee577)
Solutions
- At the call site, assert key.length === 16 before decrypt to get a clearer stack trace.
- Confirm the key source: AES-128 keys are 16 bytes; if you have a 32-byte key you need AES-256, not this decryptor.
- If the key is hex/base64 encoded, decode it (Buffer.from(k, 'hex'|'base64')) before passing.
- Check for accidental string-vs-Buffer confusion — passing the string form of a key makes length = 32 (hex) or more.
Example fix
// before decryptor.decrypt(seg, keyFromString, iv) // keyFromString is 32 chars // after const key = Buffer.from(keyFromString, 'hex') // 16 bytes decryptor.decrypt(seg, new Uint8Array(key), iv)
Defensive patterns
Strategy: validation
Validate before calling
function isAes128Key(key: Uint8Array): boolean {
return key.length === 16
}
if (!isAes128Key(key)) {
throw new Error(`key must be 16 bytes; got ${key.length}`)
}
decryptor.decrypt(ciphertext, key, iv) Type guard
function isAes128Key(key: Uint8Array): boolean {
return key instanceof Uint8Array && key.length === 16
} Try / catch
try {
decryptor.decrypt(ciphertext, key, iv)
} catch (e) {
if (/Key must be 16 bytes/.test(String(e))) {
// re-derive the key (decode hex/base64, or fetch the right one)
} else throw e
} Prevention
- Decode hex/base64 key strings to a 16-byte Uint8Array at the boundary.
- Use a branded type (e.g. type Aes128Key = Uint8Array & { __brand: 'Aes128Key' }) to force a constructor that validates length.
- Never pass raw string keys to decrypt.
When it happens
Trigger: Calling decryptor.decrypt(ciphertext, key, iv) where key.length !== 16. Typical when a 24/32-byte AES-192/256 key is passed, when a hex/base64 string was not decoded, or when a subarray view was sliced incorrectly.
Common situations: Caller stored the key as a hex string and forgot to decode; key derived with a different KDF salt/length; off-by-one in Buffer.subarray;混淆 of key and IV variables at the call site.
Related errors
- IV must be 16 bytes, got ${iv.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/fdefa2078c45351c.
Report an issue: GitHub.