d2lang/d2 · error

failed to decode %q response for %s: %w

Error message

failed to decode %q response for %s: %w

What it means

httpGet reads the response body and, if the server declared a Content-Encoding, decodes it (gzip, br, deflate). If decodeContentEncoding fails, this error wraps the underlying decode failure, naming the encoding and the URL. It means the body bytes do not match the advertised Content-Encoding header.

Source

Thrown at lib/imgbundler/imgbundler.go:245

	if err != nil {
		return nil, "", err
	}
	defer resp.Body.Close()
	l.Debug(fmt.Sprintf("fetched %s remotely - response code %v", string(href), resp.StatusCode))
	if resp.StatusCode != 200 {
		return nil, "", fmt.Errorf("expected status 200 but got %d %s", resp.StatusCode, resp.Status)
	}
	r := http.MaxBytesReader(nil, resp.Body, maxImageSize)
	buf, err := io.ReadAll(r)
	if err != nil {
		return nil, "", err
	}
	contentType := resp.Header.Get("Content-Type")
	contentEncoding := resp.Header.Get("Content-Encoding")
	if contentEncoding != "" {
		buf, err = decodeContentEncoding(buf, contentEncoding)
		if err != nil {
			return nil, "", fmt.Errorf("failed to decode %q response for %s: %w", contentEncoding, href, err)
		}
	}
	l.Debug(fmt.Sprintf("fetched content type: %s, Content length: %d bytes", contentType, len(buf)))

	return buf, contentType, nil
}

func decodeContentEncoding(buf []byte, contentEncoding string) ([]byte, error) {
	encodings := strings.Split(contentEncoding, ",")
	for i := len(encodings) - 1; i >= 0; i-- {
		encoding := strings.TrimSpace(strings.ToLower(encodings[i]))
		if encoding == "" || encoding == "identity" {
			continue
		}

		var err error
		switch encoding {
		case "gzip", "x-gzip":

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Inspect the raw response with curl --compressed to see whether the body actually matches the declared Content-Encoding
  2. Fix or bypass the misbehaving proxy/CDN layer that rewrites the body
  3. Retry the request — a truncated transfer often resolves on re-fetch
  4. If you control the origin, correct the Content-Encoding header or disable compression for the asset
Defensive patterns

Strategy: try-catch

Validate before calling

// Probe that the body decodes as advertised
resp, _ := http.Get(u)
enc := resp.Header.Get("Content-Encoding")
body, _ := io.ReadAll(resp.Body)
var probe interface{ Read([]byte) (int, error) }
switch enc {
case "", "identity":
	probe = bytes.NewReader(body)
case "gzip":
	probe, _ = gzip.NewReader(bytes.NewReader(body))
default:
	return fmt.Errorf("unsupported probe encoding %q", enc)
}

Try / catch

if _, _, err := httpGet(ctx, client, href); err != nil {
	var decodeErr = "failed to decode"
	if strings.Contains(err.Error(), decodeErr) {
		// retry without compression or log and skip this asset
	}
}

Prevention

When it happens

Trigger: Server sends Content-Encoding: gzip/br/deflate but the body is not valid data in that encoding — e.g. a proxy double-encoded or stripped the encoding, a broken/buggy origin, an antivirus middlebox rewriting bodies, or truncated compressed bodies.

Common situations: Corporate proxies and TLS-terminating LBs mangling compression, CDNs serving pre-compressed assets with wrong headers, or misconfigured web servers setting the header but sending identity content.

Understand the failure class

Related errors


AI-assisted analysis of d2lang/d2@0d69dca6f5 (2026-08-31). Data as JSON: /api/errors/159a8d592645d8cc. Report an issue: GitHub.