siyuan-note/siyuan · error

generated image redirect is not allowed

Error message

generated image redirect is not allowed

What it means

The image download HTTP client installs a CheckRedirect hook as part of SSRF protection: at most 2 redirects are followed, every redirected URL must remain https, and each hop's host must pass CheckHostSSRF. Violating any of these aborts with this error, so a provider cannot bounce the request to http:// or to an internal address.

Solutions

  1. Get the provider to return a direct, non-redirecting https image URL
  2. If a redirect chain is legitimate, increase the hop limit in generatedImageHTTPClient's CheckRedirect (only for trusted hosts)
  3. Fix the server so redirects preserve https scheme
  4. Ensure the final host is a public address that passes CheckHostSSRF

Example fix

// before
Location: "http://cdn.example.com/img.png" // https->http redirect blocked
// after
Location: "https://cdn.example.com/img.png" // allowed (scheme stays https)
Defensive patterns

Strategy: validation

Validate before calling

func nextHopOK(u *url.URL) error {
    if u.Scheme != "https" || u.Host == "" { return errors.New("bad redirect target") }
    return CheckHostSSRF(u.Hostname())
}

Try / catch

if errors.Is(err, errRedirectBlocked) || strings.Contains(err.Error(), "redirect is not allowed") {
    return fmt.Errorf("image host redirected unsafely; refusing download")
}

Prevention

When it happens

Trigger: The image URL responds with 3xx Location that is (a) the 3rd+ redirect in the chain, (b) an http:// or non-https scheme, or the next hop's hostname fails CheckHostSSRF.

Common situations: CDN redirecting https → http; redirect chains longer than 2 hops (auth gateway → CDN); redirect to an internal/private hostname (classic SSRF); expired URL redirecting to a login page over http.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/7ac31172a33e3132. Report an issue: GitHub.

Appendix: source

Thrown at kernel/util/openai.go:969

	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:       httpclient.ProxyFromEnvironment,
			DialContext: generatedImageDialer().DialContext,
		},
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			if len(via) >= 3 || req.URL.Scheme != "https" {
				return errors.New("generated image redirect is not allowed")
			}
			return CheckHostSSRF(req.URL.Hostname())
		},
	}
}

func generatedImageDialer() *net.Dialer {
	return &net.Dialer{
		Timeout: 30 * time.Second,
		Control: func(_, address string, _ syscall.RawConn) error {
			host, _, err := net.SplitHostPort(address)
			if err != nil {
				return err
			}
			ip, parseErr := netip.ParseAddr(host)
			if parseErr != nil || isUnsafeGeneratedImageIP(ip.Unmap()) {
				return errors.New("generated image URL resolved to a private or invalid IP")
			}

View on GitHub (pinned to 9f775e8a12)