siyuan-note/siyuan · error

nil response

Error message

nil response

What it means

HTTPRequest in kernel/util/httprequest.go performs an SSRF-safe outbound HTTP call for the agent's http_request tool. After ssrfSafeClient.Do returns without an error, the code defensively checks that a non-nil *http.Response was returned. Go's net/http normally guarantees a non-nil response whenever err is nil, so this error is thrown when an internal invariant of the custom SSRF-safe transport is violated and Do returns (nil, nil).

Source

Thrown at kernel/util/httprequest.go:338

	var reqBody io.Reader
	if body != "" && method != "GET" && method != "HEAD" {
		reqBody = strings.NewReader(body)
	}
	req, err := http.NewRequest(method, rawURL, reqBody)
	if err != nil {
		return 0, "", "", errors.New("invalid request: " + err.Error())
	}
	for k, v := range headers {
		req.Header.Set(k, v)
	}

	resp, err := ssrfSafeClient.Do(req)
	if err != nil {
		return 0, "", "", errors.New("request failed: " + err.Error())
	}
	if resp == nil {
		return 0, "", "", errors.New("nil response")
	}
	defer resp.Body.Close()

	statusCode = resp.StatusCode
	contentType = resp.Header.Get("Content-Type")

	maxReadBytes := int64(maxHTTPRequestBytes)
	if !isTextContentType(contentType) {
		maxReadBytes = maxHTTPRequestFileBytes
	}
	// ContentLength 为 -1(chunked)时跳过大小预检,交由 LimitReader 兜底截断。
	if resp.ContentLength > maxReadBytes {
		return statusCode, contentType, "", errors.New("response too large")
	}

	respBody, rerr := io.ReadAll(io.LimitReader(resp.Body, maxReadBytes))
	if rerr != nil {
		return statusCode, contentType, "", errors.New("read body failed: " + rerr.Error())

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Check environment proxy variables (HTTP_PROXY/HTTPS_PROXY/ALL_PROXY, NO_PROXY) and retry with the proxy disabled to confirm the proxy tunnel path is the trigger
  2. Inspect the ssrfSafeTransport.RoundTrip implementation in kernel/util/httprequest.go for a code path returning nil response with nil error and fix it to always return a non-nil error alongside a nil response
  3. Update to the latest kernel build in case the transport invariant bug has been patched
  4. Retry the request once — if it recurs deterministically, report it as a kernel bug with the target URL and proxy configuration

Example fix

// before (in a custom RoundTrip)
if somethingOdd {
    return nil, nil // violates net/http contract
}
// after
if somethingOdd {
    return nil, errors.New("transport produced no response")
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go has no pre-call validation; the invariant is internal to ssrfSafeClient.
// Sanity-check inputs before the call:
if !strings.HasPrefix(rawURL, "http://") && !strings.HasPrefix(rawURL, "https://") {
    return errors.New("URL must start with http:// or https://")
}

Type guard

if resp == nil {
    // treat as an error path before dereferencing resp
    return errors.New("nil response")
}

Try / catch

status, ct, text, err := util.HTTPRequest(method, url, headers, body)
if err != nil {
    if strings.Contains(err.Error(), "nil response") {
        // internal transport invariant violation: log and report, do not retry blindly
        return fmt.Errorf("http_request tool returned nil response: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling HTTPRequest(method, rawURL, headers, body) where the ssrfSafeClient's RoundTrip implementation returns (nil, nil) — a bug or edge case in the custom ssrfSafeTransport/proxy tunnel path rather than anything the caller can control via arguments.

Common situations: Encountered when diagnosing the proxy-tunnel transport (HTTP CONNECT or SOCKS5 path) in environments using HTTP_PROXY/HTTPS_PROXY/ALL_PROXY, or after custom modifications to ssrfSafeClient; practically never seen in normal direct-connection usage.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/cdad3583c1d674b6. Report an issue: GitHub.