gastownhall/beads · error

failed to create request: %w

Error message

failed to create request: %w

What it means

http.NewRequestWithContext failed to construct the outbound request. Since method and URL are built internally by the client, this almost always means the resolved URL string is malformed (bad base URL configuration) or the context/method combination is invalid. The error is wrapped and returned without retrying.

Source

Thrown at internal/gitlab/client.go:125

		}
		reqBody = bytes.NewReader(jsonBody)
	}

	var lastErr error
	for attempt := 0; attempt <= MaxRetries; attempt++ {
		// Reset body reader at top of loop so retries after network errors
		// don't send empty bodies (the reader may be at EOF).
		if body != nil {
			jsonBody, err := json.Marshal(body)
			if err != nil {
				return nil, nil, fmt.Errorf("failed to marshal request body: %w", err)
			}
			reqBody = bytes.NewReader(jsonBody)
		}

		req, err := http.NewRequestWithContext(ctx, method, urlStr, reqBody)
		if err != nil {
			return nil, nil, fmt.Errorf("failed to create request: %w", err)
		}

		req.Header.Set("PRIVATE-TOKEN", c.Token)
		req.Header.Set("Content-Type", "application/json")

		resp, err := c.HTTPClient.Do(req)
		if err != nil {
			lastErr = fmt.Errorf("request failed (attempt %d/%d): %w", attempt+1, MaxRetries+1, err)
			continue
		}

		// Limit response body to 50MB to prevent OOM from malformed responses.
		const maxResponseSize = 50 * 1024 * 1024
		respBody, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
		_ = resp.Body.Close() // Best effort: HTTP body close; connection may be reused regardless
		if err != nil {
			lastErr = fmt.Errorf("failed to read response (attempt %d/%d): %w", attempt+1, MaxRetries+1, err)
			continue

View on GitHub (pinned to 71377f2769)

Solutions

  1. Verify the configured base URL includes a valid scheme and host (https://gitlab.com/api/v4)
  2. Trim whitespace from URL config values read from environment or YAML
  3. Check for special characters in project/group path segments and URL-encode them
  4. Log the final urlStr by reproducing buildURL with your inputs to spot the malformed piece

Example fix

// before
BaseURL: "gitlab.example.com/api/v4"
// after
BaseURL: "https://gitlab.example.com/api/v4"
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(cfg.BaseURL)
if err != nil || u.Scheme == "" || u.Host == "" {
	return fmt.Errorf("invalid GitLab base URL: %q", cfg.BaseURL)
}

Type guard

func validBaseURL(s string) bool {
	u, err := url.Parse(s)
	return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != ""
}

Try / catch

if err != nil && strings.Contains(err.Error(), "failed to create request") {
	return fmt.Errorf("check GitLab base URL config (scheme/host): %w", err)
}

Prevention

When it happens

Trigger: A misconfigured base URL (e.g. missing scheme: 'gitlab.example.com/api/v4' instead of 'https://...'), control characters in the URL, or an invalid URL resulting from buildURL parameter concatenation, causing url.Parse inside NewRequestWithContext to fail.

Common situations: Setting GitLabURL or config base URL without the https:// scheme; trailing spaces in an env-provided URL; project/group paths containing characters that break URL parsing; proxy env vars injecting garbage.

Related errors


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