gastownhall/beads · error

create request: %w

Error message

create request: %w

What it means

http.NewRequestWithContext failed while constructing the outgoing Notion HTTP request. With a fixed method and URL scheme this is rare, and almost always means the assembled requestURL is malformed (unparseable URL) or the context is already cancelled/invalid. The error is wrapped so the net/http cause is preserved.

Source

Thrown at internal/notion/client.go:271

		httpClient = &http.Client{Timeout: DefaultTimeout}
	}

	var bodyReader io.Reader
	if requestBody != nil {
		payload, err := json.Marshal(requestBody)
		if err != nil {
			return nil, fmt.Errorf("marshal request body: %w", err)
		}
		bodyReader = bytes.NewReader(payload)
	}

	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)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error for 'parse ...' to see the malformed URL, then fix BaseURL (must be a valid absolute URL like https://api.notion.com/v1).
  2. Trim whitespace/newlines from the configured base URL before constructing the client.
  3. Leave BaseURL at its default unless you intentionally use a proxy/gateway.
  4. Test URL assembly: u, err := url.Parse(strings.TrimSuffix(baseURL, "/") + path); validate err at startup.

Example fix

// before
client := notion.NewClientWithBaseURL("https://api.notion.com/v1 
") // invalid URL

// after
baseURL := strings.TrimSpace(cfg.NotionBaseURL)
u, err := url.Parse(baseURL)
if err != nil || u.Scheme == "" {
    return fmt.Errorf("invalid notion base URL: %w", err)
}
client := notion.NewClientWithBaseURL(baseURL)
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(strings.TrimSuffix(baseURL, "/") + path)
if err != nil {
    return fmt.Errorf("invalid notion URL: %w", err)
}

Try / catch

if err != nil && strings.Contains(err.Error(), "create request") {
    log.Printf("check NOTION_BASE_URL / request path: %v", err)
}

Prevention

When it happens

Trigger: c.BaseURL misconfigured so the concatenated URL is invalid (e.g. BaseURL containing spaces or garbage); calling a wrapper with a path containing unescaped characters; passing an already-cancelled context is not it - that fails later - but an invalid URL/control characters in method or URL will trigger this.

Common situations: Config file with NOTION_BASE_URL pointing to a wrong, typo'd, or containing-invalid-characters value (spaces, newline); overriding BaseURL to a proxy URL with stray characters; tests constructing Client{BaseURL: "://bad"}.

Related errors


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