ginuerzh/gost · error

failed to create an HTTPS request: %s

Error message

failed to create an HTTPS request: %s

What it means

dohExchanger.Exchange builds a POST request to a DNS-over-HTTPS endpoint with http.NewRequestWithContext (resolver.go:894). If constructing that request fails — most often because the DoH endpoint URL is invalid or cannot be parsed — the error is wrapped as "failed to create an HTTPS request: %s" and returned before any network activity. This is a setup error: the DNS query was never sent.

Source

Thrown at resolver.go:894

			ExpectContinueTimeout: 1 * time.Second,
			DialContext:           ex.dialContext,
		},
	}

	return ex
}

func (ex *dohExchanger) dialContext(ctx context.Context, network, address string) (net.Conn, error) {
	return ex.options.chain.DialContext(ctx,
		network, address,
		TimeoutChainOption(ex.options.timeout),
	)
}

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 {

View on GitHub (pinned to a33fdbf4c9)

Solutions

  1. Log the wrapped inner error (%s) to see the underlying url.Parse failure and fix the DoH endpoint string
  2. Ensure the endpoint is a valid absolute https:// URL, e.g. https://dns.google/dns-query
  3. Verify the resolver configuration (nameserver/DoH URL) has no stray spaces, quotes, or control characters
  4. Pre-validate the URL with url.Parse in your config loader before wiring it into the resolver

Example fix

// before
endpoint := "dns.google/dns-query" // invalid: no scheme
// after
endpoint := "https://dns.google/dns-query"
Defensive patterns

Strategy: validation

Validate before calling

func validDoHEndpoint(raw string) error {
    u, err := url.Parse(raw)
    if err != nil { return fmt.Errorf("invalid DoH url %q: %w", raw, err) }
    if u.Scheme != "https" || u.Host == "" {
        return fmt.Errorf("DoH endpoint must be an absolute https URL, got %q", raw)
    }
    return nil
}

Type guard

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

Try / catch

resp, err := exchanger.Exchange(ctx, query)
if err != nil {
    if isHTTPRequestCreateError(err) {
        log.Printf("check DoH endpoint config: %v", err) // configuration bug, don't retry
        return err
    }
    return failoverToNextNameserver(err) // network-level failures may fail over
}

Prevention

When it happens

Trigger: A DoH exchanger created with a malformed endpoint URL (missing scheme, invalid characters, unparsable host) so http.NewRequestWithContext returns an error, e.g. a resolver config with a bad nameserver URL like "dns.google/dns-query" without "https://".

Common situations: Typos in the DoH URL in gost's resolver config, missing https:// scheme, control characters or spaces in the configured nameserver URL, or a config reload injecting an invalid endpoint string.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


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