gastownhall/beads · error

request failed: %w

Error message

request failed: %w

What it means

The HTTP transport itself failed: httpClient.Do returned an error before any HTTP response was received (DNS failure, connection refused/reset, TLS handshake error, timeout, or request cancellation via ctx). The error is wrapped as 'request failed: %w' with the underlying net/http error in the chain.

Source

Thrown at internal/notion/client.go:282

	requestURL := path
	if !strings.HasPrefix(requestURL, "http://") && !strings.HasPrefix(requestURL, "https://") {
		requestURL = strings.TrimSuffix(c.BaseURL, "/") + path
	}
	req, err := http.NewRequestWithContext(ctx, method, requestURL, bodyReader)
	if err != nil {
		return nil, fmt.Errorf("create request: %w", err)
	}
	req.Header.Set("Authorization", "Bearer "+c.Token)
	req.Header.Set("Notion-Version", c.NotionVersion)
	req.Header.Set("Accept", "application/json")
	if requestBody != nil {
		req.Header.Set("Content-Type", "application/json")
	}

	resp, err := httpClient.Do(req) //nolint:gosec // G704: URL is constructed from configured Notion API base, not user input
	if err != nil {
		return nil, fmt.Errorf("request failed: %w", err)
	}
	defer func() { _ = resp.Body.Close() }()

	body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes))
	if err != nil {
		return nil, fmt.Errorf("read response: %w", err)
	}
	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		return body, nil
	}

	var apiErr struct {
		Code    string `json:"code"`
		Message string `json:"message"`
	}
	if err := json.Unmarshal(body, &apiErr); err == nil && apiErr.Message != "" {
		return nil, fmt.Errorf("Notion API error %s (%d): %s", apiErr.Code, resp.StatusCode, apiErr.Message)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped error to identify the class (dial tcp / tls / context deadline) and fix accordingly.
  2. Verify outbound connectivity: curl -v https://api.notion.com/v1/users/me from the same host.
  3. Configure proxy env vars (HTTPS_PROXY) if behind a corporate proxy, or trust the corporate CA.
  4. Increase or reset the http.Client timeout used for the Notion client; retry with backoff for transient failures.

Example fix

// before
client := &notion.Client{Token: tok, HTTPClient: &http.Client{Timeout: 50 * time.Millisecond}}

// after
client := &notion.Client{Token: tok, HTTPClient: &http.Client{Timeout: 30 * time.Second}}
Defensive patterns

Strategy: retry

Validate before calling

req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.notion.com/v1/users/me", nil)
if err == nil {
    if _, err := http.DefaultClient.Do(req); err != nil {
        return fmt.Errorf("notion API unreachable: %w", err)
    }
}

Try / catch

var netErr net.Error
if errors.As(err, &netErr) && netErr.Timeout() {
    // backoff and retry
}

Prevention

When it happens

Trigger: Network unreachable or DNS cannot resolve api.notion.com; corporate proxy/firewall blocking outbound 443; TLS interception with untrusted certs; custom c.HTTPClient with a too-short timeout; context cancelled mid-request.

Common situations: Running in an air-gapped/locked-down CI environment without egress; VPN down; invalid corporate proxy not configured via HTTPS_PROXY; machine clock skew breaking TLS; custom http.Client{Timeout: 100 * time.Millisecond} in tests.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/741013bfd20d6fb2. Report an issue: GitHub.