cloudreve/cloudreve · warning · ErrBadUTF7

utf7: bad utf-7 encoding

Error message

utf7: bad utf-7 encoding

What it means

wopi.ErrBadUTF7 signals invalid modified UTF-7 encoding while decoding (or encoding) strings. Modified UTF-7 (mUTF-7, the IMAP mailbox encoding) represents non-printable characters as base64 blocks delimited by '&' and terminating '-'; the decoder rejects malformed blocks, bad base64 characters, unterminated shifted sequences, or values that decode to invalid code points.

Source

Thrown at pkg/wopi/utf7.go:80

}

func (e *simpleEncoding) NewDecoder() *encoding.Decoder {
	return &encoding.Decoder{Transformer: e.Decoder}
}

func (e *simpleEncoding) NewEncoder() *encoding.Encoder {
	return &encoding.Encoder{Transformer: e.Encoder}
}

var (
	UTF7 encoding.Encoding = &simpleEncoding{
		utf7Decoder{},
		utf7Encoder{},
	}
)

// ErrBadUTF7 is returned to indicate invalid modified UTF-7 encoding.
var ErrBadUTF7 = errors.New("utf7: bad utf-7 encoding")

// Base64 codec for code points outside of the 0x20-0x7E range.
const modifiedbase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

var u7enc = base64.NewEncoding(modifiedbase64)

func isModifiedBase64(r byte) bool {
	if r >= 'A' && r <= 'Z' {
		return true
	} else if r >= 'a' && r <= 'z' {
		return true
	} else if r >= '0' && r <= '9' {
		return true
	} else if r == '+' || r == '/' {
		return true
	}
	return false
	// bs := []byte(modifiedbase64)

View on GitHub (pinned to 20c95ad73f)

Solutions

  1. Send the field as proper modified UTF-7 (&base64-) or plain ASCII, not raw UTF-8 with stray '&' characters
  2. Escape literal '&' in mUTF-7 content as '&-'
  3. Decode with UTF7.Decoder and treat ErrBadUTF7 as 'fall back to treating the input as UTF-8/ASCII'
  4. Log the raw bytes on failure to identify double-encoding

Example fix

// before
name, err := UTF7.NewDecoder().String(raw) // raw is actually UTF-8 -> may hit ErrBadUTF7
if err != nil { return err }

// after
name, err := UTF7.NewDecoder().String(raw)
if errors.Is(err, ErrBadUTF7) {
    name = raw // input was not mUTF-7; treat as UTF-8
} else if err != nil {
    return err
}
Defensive patterns

Strategy: fallback

Validate before calling

// Cheap sanity check: only decode as mUTF-7 when '&' shifted sections look well-formed
func looksLikeMUTF7(s string) bool {
    inShift := false
    for i := 0; i < len(s); i++ {
        switch {
        case s[i] == '&':
            if inShift { return false }
            inShift = true
        case s[i] == '-':
            inShift = false
        case inShift && !isModifiedBase64Byte(s[i]):
            return false
        }
    }
    return !inShift // unterminated shift section is invalid
}

Try / catch

// Fall back to UTF-8 when mUTF-7 decoding fails
name, err := UTF7.NewDecoder().String(raw)
if errors.Is(err, ErrBadUTF7) {
    name = raw // client sent UTF-8/ASCII; use as-is
} else if err != nil {
    return err
}

Prevention

When it happens

Trigger: A WOPI client sends a filename or mailbox-style path encoded in modified UTF-7 where a '&' shifted section contains characters outside the modified base64 alphabet, is never closed with '-', decodes to a rune sequence with surrogate issues, or the encoder is handed a rune it cannot represent. Decoding a plain UTF-8 string that happens to contain '&' followed by base64-looking characters also trips it.

Common situations: Integrating a WOPI office suite that URL-encodes names differently than expected; filenames with emoji/CJK characters double-encoded (UTF-8 bytes fed to the mUTF-7 decoder); protocol middleware corrupting '&' characters; clients sending UTF-7 where UTF-8 was required.


AI-assisted analysis of cloudreve/cloudreve@20c95ad73f (2026-08-16). Data as JSON: /api/errors/bde99dd39e81c5d6. Report an issue: GitHub.