AlistGo/alist · error
step3 response layer2 aes ecb decrypt failed: %w
Error message
step3 response layer2 aes ecb decrypt failed: %w
What it means
The second decryption layer of the SSO response failed: the hex-decoded bytes could not be decrypted with AES-ECB using KEY_HEX_2. The ciphertext was not produced with the expected static key — a strong signal the server-side protocol/keys changed or the payload is an error blob.
Source
Thrown at drivers/139/util.go:1227
}
hexInner := jsoniter.Get(decryptedLayer1StrBytes, "data").ToString()
if hexInner == "" {
return "", errors.New("missing data field in first layer decryption result")
}
log.Debugf("DEBUG: 第一层解密提取到 hex_inner, length=%d", len(hexInner))
key2, err := hex.DecodeString(KEY_HEX_2)
if err != nil {
return "", fmt.Errorf("failed to decode KEY_HEX_2: %w", err)
}
hexInnerBytes, err := hex.DecodeString(hexInner)
if err != nil {
return "", fmt.Errorf("failed to decode hex_inner: %w", err)
}
finalJsonStrBytes, err := aesEcbDecrypt(hexInnerBytes, key2)
if err != nil {
return "", fmt.Errorf("step3 response layer2 aes ecb decrypt failed: %w", err)
}
log.Debugf("DEBUG: third party login response decrypted.")
authToken := jsoniter.Get(finalJsonStrBytes, "authToken").ToString()
if authToken == "" {
return "", errors.New("failed to extract authToken from final decryption result")
}
account := jsoniter.Get(finalJsonStrBytes, "account").ToString()
userDomainId := jsoniter.Get(finalJsonStrBytes, "userDomainId").ToString()
if account == "" || userDomainId == "" {
return "", errors.New("failed to extract account or userDomainId from final decryption result")
}
d.Account = account
d.UserDomainID = userDomainId
return base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("pc:%s:%s", account, authToken))), nil
}View on GitHub (pinned to 843d9dc814)
Solutions
- Update OpenList to the newest release — key constants (KEY_HEX_1/KEY_HEX_2) and headers are re-captured when the app protocol changes
- Re-capture the traffic from the current official android client and update KEY_HEX_2 in a local fork if you cannot wait upstream
- Verify the whole flow with fresh cookies to rule out error payloads masquerading as ciphertext
- Report upstream with the failing step and app version if persistent
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check ciphertext block alignment before ECB decrypt
if len(hexInnerBytes) == 0 || len(hexInnerBytes)%aes.BlockSize != 0 {
return fmt.Errorf("layer2 ciphertext misaligned (%d bytes): protocol drift", len(hexInnerBytes))
} Type guard
func isLayer2DecryptErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "layer2 aes ecb decrypt failed")
} Try / catch
// Never auto-retry; refresh keys or update the driver
if isLayer2DecryptErr(err) {
return fmt.Errorf("139 SSO keys outdated — update OpenList or re-capture KEY_HEX_2: %w", err)
} Prevention
- Subscribe to OpenList releases; key rotations are patched upstream quickly
- When self-capturing keys, record the android app version alongside KEY_HEX_1/2
- Treat ECB-decrypt failures as hard protocol errors, not transient ones
When it happens
Trigger: aesEcbDecrypt rejects the data (bad block size or unpadding failure) after hexInner decoded fine; typically after China Mobile rotates keys or alters the layer-2 scheme in a newer app build.
Common situations: OpenList driver version lagging behind a 139 cloud app update (keys/headers captured from old app builds), or an upstream error body accidentally passing earlier checks.
Related errors
- failed to decode hex_inner: %w
- failed to get original hash: %w
- failed to re-encrypt node passphrase: %w
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/c62f78f7b4116e29.
Report an issue: GitHub.