golang/go · error
gzip.Write: non-Latin-1 header string
Error message
gzip.Write: non-Latin-1 header string
What it means
gzip.Writer.writeString rejects any string containing a NUL byte (0x00) or a rune above 0xff because GZIP header strings (FNAME, FCOMMENT) are NUL-terminated ISO 8859-1 Latin-1 per RFC 1952. A NUL would terminate the string early and a non-Latin-1 rune cannot be represented.
Source
Thrown at src/compress/gzip/gzip.go:121
return errors.New("gzip.Write: Extra data is too large")
}
le.PutUint16(z.buf[:2], uint16(len(b)))
_, err := z.w.Write(z.buf[:2])
if err != nil {
return err
}
_, err = z.w.Write(b)
return err
}
// writeString writes a UTF-8 string s in GZIP's format to z.w.
// GZIP (RFC 1952) specifies that strings are NUL-terminated ISO 8859-1 (Latin-1).
func (z *Writer) writeString(s string) (err error) {
// GZIP stores Latin-1 strings; error if non-Latin-1; convert if non-ASCII.
needconv := false
for _, v := range s {
if v == 0 || v > 0xff {
return errors.New("gzip.Write: non-Latin-1 header string")
}
if v > 0x7f {
needconv = true
}
}
if needconv {
b := make([]byte, 0, len(s))
for _, v := range s {
b = append(b, byte(v))
}
_, err = z.w.Write(b)
} else {
_, err = io.WriteString(z.w, s)
}
if err != nil {
return err
}
// GZIP strings are NUL-terminated.View on GitHub (pinned to b6b368adc5)
Solutions
- Sanitize Header.Name and Header.Comment: drop or replace runes > 0xFF and any NUL.
- Encode the original filename with percent-encoding or base32 so it stays inside the ASCII subset of Latin-1.
- Store the full Unicode filename in a sidecar metadata file instead of the GZIP header.
- If you must preserve Unicode, transcode the string to ISO 8859-1 only when every rune is <= 0xFF; otherwise reject upfront.
Example fix
// before
gz.Header.Name = path.Base(userSuppliedFilename) // may contain emoji
// after: keep only Latin-1, substitute everything else
gz.Header.Name = sanitizeLatin1(userSuppliedFilename)
func sanitizeLatin1(s string) string {
var b strings.Builder
for _, r := range s {
if r == 0 || r > 0xff { r = '_' }
b.WriteRune(r)
}
return b.String()
} Defensive patterns
Strategy: validation
Validate before calling
func latin1Safe(s string) (string, error) {
for _, r := range s {
if r == 0 || r > 0xff {
return "", fmt.Errorf("rune U+%04X not representable in Latin-1", r)
}
}
return s, nil
}
func setGzipName(h *gzip.Header, name string) error {
safe, err := latin1Safe(name)
if err != nil { return err }
h.Name = safe
return nil
} Try / catch
if err := setGzipName(&gz.Header, userFilename); err != nil {
// Fall back to an ASCII slug or store filename out-of-band.
gz.Header.Name = asciiSlug(userFilename)
} Prevention
- Sanitize user-supplied filenames before assigning to Header.Name.
- Percent-encode or base32-encode Unicode filenames to stay in the ASCII subset.
- Test with CJK, emoji, and NFD-decomposed samples in your test fixtures.
- Audit every place that sets Header.Comment for the same restriction.
When it happens
Trigger: Setting gz.Header.Name or gz.Header.Comment to a value containing a NUL byte or any rune above U+00FF (e.g., emoji, CJK characters, accented Cyrillic, right-to-left scripts), then calling Write/Flush/Close.
Common situations: Storing user-supplied UTF-8 filenames (Android downloads with CJK names, macOS NFD-decomposed accents) in the GZIP FNAME field; copying a Comment from a JSON or YAML manifest that contains em-dashes, smart quotes, or emoji.
Related errors
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/056bfc1ce0df916f.
Report an issue: GitHub.