d2lang/d2 · error

expected status 200 but got %d %s

Error message

expected status 200 but got %d %s

What it means

httpGet fetches a remote image over HTTP and expects a successful response. When the HTTP status code is anything other than 200, it aborts and returns this error carrying the actual status code and status text. The library only accepts a plain 200 — redirects are already followed by the client, but soft errors like 404/403/500 are surfaced here.

Source

Thrown at lib/imgbundler/imgbundler.go:233

	req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36")
	req.Header.Set("Accept", "image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8")
	req.Header.Set("Accept-Language", "en-US,en;q=0.9")
	req.Header.Set("Accept-Encoding", "gzip, deflate, br")
	req.Header.Set("DNT", "1")
	req.Header.Set("Upgrade-Insecure-Requests", "1")
	req.Header.Set("Sec-Fetch-Dest", "image")
	req.Header.Set("Sec-Fetch-Mode", "no-cors")
	req.Header.Set("Sec-Fetch-Site", "cross-site")

	resp, err := httpClient.Do(req)
	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
}

View on GitHub (pinned to 0d69dca6f5)

Solutions

  1. Open the URL in a browser/curl to confirm the actual status and fix or replace the image URL
  2. Set a browser-like User-Agent and any required Referer/auth headers if the origin blocks hotlinking
  3. Retry with backoff on 429/5xx; the cause is transient server-side limiting or failure
  4. If the image is gone, remove it from the page or substitute a valid hosted copy

Example fix

// before
resp, _, err := httpGet(ctx, client, href)
// after
// Check the URL first; on transient statuses retry
resp, _, err := httpGet(ctx, client, href)
if err != nil && (strings.Contains(err.Error(), "429") || strings.Contains(err.Error(), "50")) {
	time.Sleep(backoff)
	resp, _, err = httpGet(ctx, client, href)
}
Defensive patterns

Strategy: retry

Validate before calling

resp, err := http.Head(imgURL)
if err != nil || resp.StatusCode != 200 {
	return fmt.Errorf("image %s not fetchable: status %v", imgURL, resp.StatusCode)
}

Try / catch

if _, _, err := httpGet(ctx, client, href); err != nil {
	if strings.Contains(err.Error(), "expected status 200") {
		// inspect status code inside message; retry on 429/5xx, skip URL on 404/403
	}
}

Prevention

When it happens

Trigger: The remote image URL returned a non-200 status: 404 (image moved/deleted), 403 (hotlink protection, missing auth, UA blocking), 5xx (origin/CDN error), or 429 (rate limiting). Raised in httpGet, called by worker during page image bundling.

Common situations: Bundling pages whose images point at expired CDN links, sites blocking non-browser user agents, signed URLs whose tokens expired, or origins rate-limiting the bundler after many requests.

Related errors


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