spicetify/cli · error

error decoding UTF-16LE content

Error message

error decoding UTF-16LE content: %w

What it means

After slicing out the marker-delimited region, ReadStringFromUTF16Binary passes those bytes to decodeUTF16LE to convert UTF-16LE to UTF-8. If decoding fails, the error is wrapped as "error decoding UTF-16LE content: %w". Currently the only underlying failure decodeUTF16LE produces is an odd-length byte slice, so this error almost always means the extracted region has an odd number of bytes.

Solutions

  1. Inspect the bytes between the markers to confirm the region is a whole number of UTF-16 code units (even byte count).
  2. Check that start/end markers are distinct and non-overlapping so the slice boundary math is correct.
  3. Re-obtain the file from the application rather than using a hand-edited or partially converted copy.
  4. Unwrap the %w error to confirm it is the odd-length case, then validate the file's UTF-16LE integrity before parsing.

Example fix

// before: blind parse of a suspicious file
val, _, _, err := utils.ReadStringFromUTF16Binary(prefs, start, end)
// after: sanity-check file size is even after the 2-byte BOM first
if fi, _ := os.Stat(prefs); fi != nil && fi.Size()%2 != 0 {
	log.Fatal("prefs file has odd size; not valid UTF-16LE")
}
val, _, _, err := utils.ReadStringFromUTF16Binary(prefs, start, end)
Defensive patterns

Strategy: validation

Validate before calling

// Validate the region is decodable UTF-16 (even byte length) before/after extraction
func validUTF16Region(data []byte) bool { return len(data)%2 == 0 }

Type guard

// Go: narrow via error unwrap
func isDecodeErr(err error) bool {
	return err != nil && strings.Contains(err.Error(), "error decoding UTF-16LE content")
}

Try / catch

val, _, _, err := utils.ReadStringFromUTF16Binary(prefs, start, end)
if err != nil {
	var base error = err
	if errors.Unwrap(err) != nil { base = errors.Unwrap(err) }
	return fmt.Errorf("utf16 decode failed (%v); file may be corrupt", base)
}

Prevention

When it happens

Trigger: Calling ReadStringFromUTF16Binary when the bytes between (and including) the start and end markers have odd length. Note the slicing arithmetic at file-utils.go:49 uses endIdx relative to the search space combined with marker lengths, so overlapping/duplicated markers or a malformed region can produce an odd-length slice that fails UTF-16 decoding.

Common situations: A file edited by hand where the delimiters no longer pair up cleanly; start and end markers that overlap or occur an odd number of bytes apart; a file that was partially converted between encodings so the region contains stray single bytes; using markers that themselves contain multi-byte surrogate characters and mismatched pairs.

Related errors


AI-assisted analysis of spicetify/cli@1f13f73616 (2026-08-31). Data as JSON: /api/errors/a9e5e96821ae0d10. Report an issue: GitHub.

Appendix: source

Thrown at src/utils/file-utils.go:53

	searchStartMarker = encodeUTF16LE(startMarker)
	searchEndMarker = encodeUTF16LE(endMarker)

	startIdx = bytes.Index(contentToSearch, searchStartMarker)
	if startIdx == -1 {
		return "", -1, -1, fmt.Errorf("start marker not found: %s", string(startMarker))
	}

	searchSpace := contentToSearch[startIdx+len(searchStartMarker):]
	endIdx = bytes.Index(searchSpace, searchEndMarker)
	if endIdx == -1 {
		return "", -1, -1, fmt.Errorf("end marker not found after start index %d: %s", startIdx+len(searchStartMarker), string(endMarker))
	}

	stringContentBytes := contentToSearch[startIdx : startIdx+len(searchStartMarker)+endIdx+len(searchEndMarker)]

	decodedStringBytes, err := decodeUTF16LE(stringContentBytes)
	if err != nil {
		return "", -1, -1, fmt.Errorf("error decoding UTF-16LE content: %w", err)
	}

	// Adjust indices to be byte offsets in the original file
	originalStartIdx := 2 + startIdx
	originalEndIdx := 2 + endIdx + len(stringContentBytes)
	return string(decodedStringBytes), originalStartIdx, originalEndIdx, nil
}

// Helper function to encode a byte slice (assumed UTF-8) to UTF-16LE
func encodeUTF16LE(data []byte) []byte {
	utf16Bytes := utf16.Encode([]rune(string(data)))
	byteSlice := make([]byte, len(utf16Bytes)*2)
	for i, r := range utf16Bytes {
		binary.LittleEndian.PutUint16(byteSlice[i*2:], r)
	}

	return byteSlice
}

View on GitHub (pinned to 1f13f73616)