AlistGo/alist · error

failed to decode hex_inner: %w

Error message

failed to decode hex_inner: %w

What it means

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.

Source

Thrown at drivers/139/util.go:1223

		"User-Agent":          "okhttp/3.12.2",
	}, KEY_HEX_1, nil)
	if err != nil {
		return "", fmt.Errorf("step3 encrypted request failed: %w", err)
	}

	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")
	}

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Log the hexInner string content and compare with a capture from the official android client
  2. Refresh credentials/cookies and retry — stale session state often makes the server answer with a non-payload body
  3. Update OpenList to the latest version where the layered SSO scheme is maintained in sync with the mobile app
  4. If maintaining a fork, hex-validate with a lenient decoder to identify what encoding the field actually switched to
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight: reject non-hex data before decryption attempt
if !isHex(hexInner) {
	return fmt.Errorf("data field is not hex: %q", truncate(hexInner, 32))
}

Type guard

func isHex(s string) bool {
	if len(s)%2 != 0 { return false }
	for _, c := range s {
		if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { return false }
	}
	return true
}

Try / catch

// Classify as protocol drift and capture the payload; do not retry blind
if strings.Contains(err.Error(), "failed to decode hex_inner") {
	log.Printf("layer-1 data field not hex — protocol change suspected")
	return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/74a5b7b14dc9d9c3. Report an issue: GitHub.