{"record":{"id":"3a7e14f0194a6c22","repo":"ginuerzh/gost","slug":"failed-to-perform-an-https-request-s","errorCode":null,"errorMessage":"failed to perform an HTTPS request: %s","messagePattern":"failed to perform an HTTPS request: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"resolver.go","lineNumber":907,"sourceCode":"}\n\nfunc (ex *dohExchanger) Exchange(ctx context.Context, query []byte) ([]byte, error) {\n\treq, err := http.NewRequestWithContext(ctx, \"POST\", ex.endpoint.String(), bytes.NewBuffer(query))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create an HTTPS request: %s\", err)\n\t}\n\n\t// req.Header.Add(\"Content-Type\", \"application/dns-udpwireformat\")\n\treq.Header.Add(\"Content-Type\", \"application/dns-message\")\n\treq.Host = ex.endpoint.Hostname()\n\n\tclient := ex.client\n\tif client == nil {\n\t\tclient = http.DefaultClient\n\t}\n\tresp, err := client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to perform an HTTPS request: %s\", err)\n\t}\n\n\t// Check response status code\n\tdefer resp.Body.Close()\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn nil, fmt.Errorf(\"returned status code %d\", resp.StatusCode)\n\t}\n\n\t// Read wireformat response from the body\n\tbuf, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read the response body: %s\", err)\n\t}\n\n\treturn buf, nil\n}\n","sourceCodeStart":889,"sourceCodeEnd":924,"githubUrl":"https://github.com/ginuerzh/gost/blob/a33fdbf4c98034f4bfeeaea9868909822b9c526d/resolver.go#L889-L924","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the DoH endpoint URL is correct and reachable (curl -v the endpoint, check DNS and connectivity)","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","Inspect the custom dialer chain passed via ExchangerOption — if it fails, Exchange reports here","Ensure the context passed to Exchange is not already cancelled and has sufficient deadline","Check for an HTTP(S)_PROXY environment mismatch — the transport explicitly does not use ProxyFromEnvironment"],"exampleFix":"// before\nex := NewDoHExchanger(endpointURL, tlsConfig) // default/short timeout, unreachable endpoint\nresp, err := ex.Exchange(ctx, query) // failed to perform an HTTPS request: ...context deadline exceeded\n// after\nex := NewDoHExchanger(endpointURL, tlsConfig, WithTimeout(10*time.Second)) // reachable endpoint, sane timeout\nresp, err := ex.Exchange(ctx, query)","handlingStrategy":"retry","validationCode":"u, err := url.Parse(dohURL)\nif err != nil || u.Scheme != \"https\" || u.Host == \"\" {\n    return fmt.Errorf(\"invalid DoH endpoint: %q\", dohURL)\n}\nconn, err := net.DialTimeout(\"tcp\", net.JoinHostPort(u.Hostname(), \"443\"), 5*time.Second)\nif err != nil {\n    return fmt.Errorf(\"DoH endpoint unreachable: %w\", err)\n}\nconn.Close()","typeGuard":"func isHTTPRequestFailure(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"failed to perform an HTTPS request:\")\n}","tryCatchPattern":"buf, err := exchanger.Exchange(ctx, query)\nif err != nil {\n    if isHTTPRequestFailure(err) {\n        if errors.Is(ctx.Err(), context.DeadlineExceeded) {\n            // increase timeout and retry with backoff\n        }\n        return fmt.Errorf(\"doh transport failure: %w\", err)\n    }\n    return err\n}","preventionTips":["Reachability-test the DoH endpoint (TCP dial + TLS handshake) at startup","Set a generous, explicit timeout via ExchangerOption instead of relying on defaults","Verify the endpoint URL scheme is https and the host resolves","Ensure the custom dialer chain (ExchangerOption) works before wiring it into the exchanger","Check firewall/proxy rules allow egress to the DoH provider"],"tags":["network","dns","https","go","doh"],"backgroundTag":"http-request-failed","analyzedSha":"a33fdbf4c98034f4bfeeaea9868909822b9c526d","analyzedAt":"2026-09-02T22:15:54.506Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}