siyuan-note/siyuan · error

download generated image failed with status %d

Error message

download generated image failed with status %d

What it means

Thrown by downloadGeneratedImage (kernel/util/openai.go:799) when the HTTP response status code is outside the 2xx range. It fires after the SSRF and redirect guards pass, so it specifically indicates the remote image host refused or failed the GET.

Source

Thrown at kernel/util/openai.go:799

	parsed, err := url.Parse(rawURL)
	if err != nil || parsed.Scheme != "https" || parsed.Host == "" {
		return nil, errors.New("generated image URL must use HTTPS")
	}
	if err = CheckHostSSRF(parsed.Hostname()); err != nil {
		return nil, err
	}
	client := generatedImageHTTPClient()
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil)
	if err != nil {
		return nil, err
	}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return nil, fmt.Errorf("download generated image failed with status %d", resp.StatusCode)
	}
	if resp.ContentLength > maxGeneratedImageBytes {
		return nil, errors.New("generated image exceeds size limit")
	}
	data, err := io.ReadAll(io.LimitReader(resp.Body, maxGeneratedImageBytes+1))
	if err != nil {
		return nil, err
	}
	if len(data) > maxGeneratedImageBytes {
		return nil, errors.New("generated image exceeds size limit")
	}
	return data, nil
}

func generatedImageHTTPClient() *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			Proxy:       http.ProxyFromEnvironment,

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Retry the download immediately — most 4xx/5xx from signed CDN URLs are transient.
  2. If persistently 403, request b64_json instead of URL delivery so there is no TTL window.
  3. Shorten the gap between CreateImage and the download (avoid long work between them).
  4. Check provider status and rate limits if 429/5xx repeats.

Example fix

// before
data, err = downloadGeneratedImage(requestCtx, result.URL)
if err != nil { return GeneratedImage{}, err }

// after (bounded retry on transient download failures)
var data []byte
for attempt := 1; attempt <= 3; attempt++ {
    data, err = downloadGeneratedImage(requestCtx, result.URL)
    if err == nil { break }
    logging.LogWarnf("image download attempt %d failed: %s", attempt, err)
    if attempt < 3 { time.Sleep(time.Duration(attempt) * time.Second) }
}
if err != nil { return GeneratedImage{}, err }
Defensive patterns

Strategy: retry

Validate before calling

// Cannot pre-check a remote status code; pre-flight belongs to retry logic — see tryCatchPattern.

Try / catch

var data []byte
for attempt := 1; attempt <= 3; attempt++ {
    var derr error
    data, derr = downloadGeneratedImage(ctx, result.URL)
    if derr == nil { break }
    if strings.Contains(derr.Error(), "failed with status") && attempt < 3 {
        time.Sleep(time.Duration(attempt) * time.Second)
        continue
    }
    return GeneratedImage{}, derr
}

Prevention

When it happens

Trigger: GET against the model-returned URL returns 403 (signed URL expired), 404 (object gone), 429 (rate limited), or 5xx (CDN/provider error). Generated-image URLs from providers like OpenAI are typically short-lived signed links, so 403 after a delay is the classic case.

Common situations: The download started after the signed URL's TTL expired (slow model + slow pipeline); CDN outage; rate-limit burst; provider revoked the URL; redirect chain that ended at an error page.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/981608f5a6ef4a2c. Report an issue: GitHub.