micro/go-micro · error

failed to create request: %w

Error message

failed to create request: %w

What it means

callAPI failed constructing the http.Request for POST /v1beta/models/<model>:generateContent, typically because the composed URL was invalid or the context was already canceled. The wrapped %w error distinguishes url construction issues from context cancellation.

Source

Thrown at ai/gemini/gemini.go:295

	if s.closed {
		return nil
	}
	s.closed = true
	return s.body.Close()
}

func (p *Provider) callAPI(ctx context.Context, req map[string]any) (*ai.Response, []map[string]any, error) {
	reqBody, err := json.Marshal(req)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	apiURL := strings.TrimRight(p.opts.BaseURL, "/") +
		"/v1beta/models/" + p.opts.Model + ":generateContent"

	httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, bytes.NewReader(reqBody))
	if err != nil {
		return nil, nil, fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("x-goog-api-key", p.opts.APIKey)

	httpResp, err := http.DefaultClient.Do(httpReq)
	if err != nil {
		return nil, nil, fmt.Errorf("API request failed: %w", err)
	}
	defer httpResp.Body.Close()

	respBody, _ := io.ReadAll(httpResp.Body)
	if httpResp.StatusCode != http.StatusOK {
		return nil, nil, ai.NewHTTPError(httpResp, respBody)
	}

	var geminiResp struct {
		Candidates []struct {

View on GitHub (pinned to 24529f1404)

Solutions

  1. Sanitize opts.Model and opts.BaseURL: trim whitespace and confirm BaseURL parses via net/url.Parse with a valid scheme.
  2. If the wrapped error is 'context canceled', audit the caller's context lifecycle and deadlines.
  3. Log the composed URL to spot path-unsafe characters from the model name.
  4. Validate configuration at startup rather than at request time.

Example fix

// before
provider, _ := gemini.NewProvider(gemini.WithBaseURL(cfg.Endpoint))
// after
u, err := url.Parse(cfg.Endpoint)
if err != nil || (u.Scheme != "http" && u.Scheme != "https") {
    return fmt.Errorf("invalid gemini BaseURL: %q", cfg.Endpoint)
}
provider, _ := gemini.NewProvider(gemini.WithBaseURL(strings.TrimRight(cfg.Endpoint, " ")))
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(strings.TrimSpace(baseURL))
if err != nil || u.Host == "" || (u.Scheme != "http" && u.Scheme != "https") {
    return fmt.Errorf("invalid gemini BaseURL: %q", baseURL)
}
if strings.TrimSpace(model) != model || model == "" {
    return fmt.Errorf("invalid gemini model: %q", model)
}

Type guard

func validCallAPIConfig(baseURL, model string) bool {
    u, err := url.Parse(baseURL)
    return err == nil && u.Host != "" && (u.Scheme == "http" || u.Scheme == "https") &&
        model != "" && strings.TrimSpace(model) == model
}

Try / catch

resp, err := provider.Generate(ctx, req)
if err != nil {
    if strings.Contains(err.Error(), "failed to create request") {
        if ctx.Err() != nil { return fmt.Errorf("canceled: %w", ctx.Err()) }
        return fmt.Errorf("bad provider config (check BaseURL/model): %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling a non-streaming Gemini method with an opts.Model or opts.BaseURL that yields an unparsable URL (spaces, control characters, missing scheme), or with an already-canceled ctx.

Common situations: Model name from env containing a newline/trailing space; BaseURL misconfigured for a self-hosted gateway without scheme; parent context canceled before the call executes.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/c495eb0535937f15. Report an issue: GitHub.