ginuerzh/gost · error

failed to perform an HTTPS request: %s

Error message

failed to perform an HTTPS request: %s

What it means

This error wraps any failure returned by the HTTP client's Do() call during a DNS-over-HTTPS exchange in dohExchanger.Exchange (resolver.go:907). It means the HTTPS POST carrying the DNS wire-format query never completed — the failure happened at the transport level (connection, TLS handshake, timeout, cancellation) before a response status code could even be read. The underlying net/http error is embedded via %s.

Source

Thrown at resolver.go:907

}

func (ex *dohExchanger) Exchange(ctx context.Context, query []byte) ([]byte, error) {
	req, err := http.NewRequestWithContext(ctx, "POST", ex.endpoint.String(), bytes.NewBuffer(query))
	if err != nil {
		return nil, fmt.Errorf("failed to create an HTTPS request: %s", err)
	}

	// req.Header.Add("Content-Type", "application/dns-udpwireformat")
	req.Header.Add("Content-Type", "application/dns-message")
	req.Host = ex.endpoint.Hostname()

	client := ex.client
	if client == nil {
		client = http.DefaultClient
	}
	resp, err := client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to perform an HTTPS request: %s", err)
	}

	// Check response status code
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("returned status code %d", resp.StatusCode)
	}

	// Read wireformat response from the body
	buf, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read the response body: %s", err)
	}

	return buf, nil
}

View on GitHub (pinned to a33fdbf4c9)

Solutions

  1. Verify the DoH endpoint URL is correct and reachable (curl -v the endpoint, check DNS and connectivity)
  2. Check the embedded wrapped error (%s) for the root cause: timeouts -> increase ExchangerOption timeout; x509 errors -> fix tlsConfig or system roots; connection refused -> fix network/proxy
  3. Inspect the custom dialer chain passed via ExchangerOption — if it fails, Exchange reports here
  4. Ensure the context passed to Exchange is not already cancelled and has sufficient deadline
  5. Check for an HTTP(S)_PROXY environment mismatch — the transport explicitly does not use ProxyFromEnvironment

Example fix

// before
ex := NewDoHExchanger(endpointURL, tlsConfig) // default/short timeout, unreachable endpoint
resp, err := ex.Exchange(ctx, query) // failed to perform an HTTPS request: ...context deadline exceeded
// after
ex := NewDoHExchanger(endpointURL, tlsConfig, WithTimeout(10*time.Second)) // reachable endpoint, sane timeout
resp, err := ex.Exchange(ctx, query)
Defensive patterns

Strategy: retry

Validate before calling

u, err := url.Parse(dohURL)
if err != nil || u.Scheme != "https" || u.Host == "" {
    return fmt.Errorf("invalid DoH endpoint: %q", dohURL)
}
conn, err := net.DialTimeout("tcp", net.JoinHostPort(u.Hostname(), "443"), 5*time.Second)
if err != nil {
    return fmt.Errorf("DoH endpoint unreachable: %w", err)
}
conn.Close()

Type guard

func isHTTPRequestFailure(err error) bool {
    return err != nil && strings.Contains(err.Error(), "failed to perform an HTTPS request:")
}

Try / catch

buf, err := exchanger.Exchange(ctx, query)
if err != nil {
    if isHTTPRequestFailure(err) {
        if errors.Is(ctx.Err(), context.DeadlineExceeded) {
            // increase timeout and retry with backoff
        }
        return fmt.Errorf("doh transport failure: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Exchange() on a DoH exchanger created with NewDoHExchanger when the client.Do(req) call fails: the DoH endpoint host is unreachable, DNS for the endpoint fails, the TLS handshake fails (bad tlsConfig, expired certs), the request context is cancelled, options.timeout elapses, or the configured chain DialContext fails to establish the underlying connection.

Common situations: DoH resolver URL misconfigured or pointing at a down server; corporate firewall/proxy blocking the DoH endpoint; custom dialer chain (options.chain) failing to connect; short timeout on slow networks; endpoint HTTPS certificate errors; program shutting down and cancelling the context mid-query.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02). Data as JSON: /api/errors/3a7e14f0194a6c22. Report an issue: GitHub.