{"record":{"id":"74a5b7b14dc9d9c3","repo":"AlistGo/alist","slug":"failed-to-decode-hex-inner-w","errorCode":null,"errorMessage":"failed to decode hex_inner: %w","messagePattern":"failed to decode hex_inner: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/139/util.go","lineNumber":1223,"sourceCode":"\t\t\"User-Agent\":          \"okhttp/3.12.2\",\n\t}, KEY_HEX_1, nil)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"step3 encrypted request failed: %w\", err)\n\t}\n\n\thexInner := jsoniter.Get(decryptedLayer1StrBytes, \"data\").ToString()\n\tif hexInner == \"\" {\n\t\treturn \"\", errors.New(\"missing data field in first layer decryption result\")\n\t}\n\tlog.Debugf(\"DEBUG: 第一层解密提取到 hex_inner, length=%d\", len(hexInner))\n\n\tkey2, err := hex.DecodeString(KEY_HEX_2)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to decode KEY_HEX_2: %w\", err)\n\t}\n\thexInnerBytes, err := hex.DecodeString(hexInner)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to decode hex_inner: %w\", err)\n\t}\n\tfinalJsonStrBytes, err := aesEcbDecrypt(hexInnerBytes, key2)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"step3 response layer2 aes ecb decrypt failed: %w\", err)\n\t}\n\tlog.Debugf(\"DEBUG: third party login response decrypted.\")\n\n\tauthToken := jsoniter.Get(finalJsonStrBytes, \"authToken\").ToString()\n\tif authToken == \"\" {\n\t\treturn \"\", errors.New(\"failed to extract authToken from final decryption result\")\n\t}\n\n\taccount := jsoniter.Get(finalJsonStrBytes, \"account\").ToString()\n\tuserDomainId := jsoniter.Get(finalJsonStrBytes, \"userDomainId\").ToString()\n\tif account == \"\" || userDomainId == \"\" {\n\t\treturn \"\", errors.New(\"failed to extract account or userDomainId from final decryption result\")\n\t}\n","sourceCodeStart":1205,"sourceCodeEnd":1241,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/139/util.go#L1205-L1241","documentation":"During step 3 SSO login, the 'data' field extracted from the first decryption layer was expected to be a hex-encoded AES-ECB ciphertext, but hex.DecodeString rejected it. The server returned a data blob that is not hex — usually because the first-layer response was an error message rather than the login payload.","triggerScenarios":"jsoniter extracted a non-empty 'data' field, but it contains non-hex characters (e.g. a base64 string, a URL, or an error description) because the SSO request was rejected upstream.","commonSituations":"Protocol drift after a 139 app update changing the inner encoding, expired step-2 token producing an error body that still has a 'data' field, or partial response corruption.","solutions":["Log the hexInner string content and compare with a capture from the official android client","Refresh credentials/cookies and retry — stale session state often makes the server answer with a non-payload body","Update OpenList to the latest version where the layered SSO scheme is maintained in sync with the mobile app","If maintaining a fork, hex-validate with a lenient decoder to identify what encoding the field actually switched to"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Pre-flight: reject non-hex data before decryption attempt\nif !isHex(hexInner) {\n\treturn fmt.Errorf(\"data field is not hex: %q\", truncate(hexInner, 32))\n}","typeGuard":"func isHex(s string) bool {\n\tif len(s)%2 != 0 { return false }\n\tfor _, c := range s {\n\t\tif !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { return false }\n\t}\n\treturn true\n}","tryCatchPattern":"// Classify as protocol drift and capture the payload; do not retry blind\nif strings.Contains(err.Error(), \"failed to decode hex_inner\") {\n\tlog.Printf(\"layer-1 data field not hex — protocol change suspected\")\n\treturn err\n}","preventionTips":["Log the data field content when it fails hex validation","Track 139 app version changes; the layered hex/ECB scheme changes with app updates","Maintain a fork-ready location for the SSO constants so patches are one-line"],"tags":["encoding","hex","sso","protocol"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}