multica-ai/multica · error · ErrInvalidBaseURL

%w: %s

Error message

%w: %s

What it means

Cloudruntime Client.doInner rejects the configured base URL: url.Parse failed or the parsed URL lacks a scheme/host, and the error wraps the sentinel ErrInvalidBaseURL. The '%w: %s' format preserves errors.Is(err, ErrInvalidBaseURL) while showing the offending value, so callers can distinguish misconfiguration from transport failures.

Source

Thrown at server/internal/cloudruntime/client.go:129

func (c *Client) Do(ctx context.Context, req Request) (*Response, error) {
	if c == nil || c.baseURL == "" {
		return nil, ErrDisabled
	}

	op := inferCloudRuntimeOp(req.Op, req.Method, req.Path)
	start := time.Now()
	resp, err := c.doInner(ctx, req)
	if c.recorder != nil {
		status := requestStatusBucket(resp, err)
		c.recorder.RecordCloudRuntimeRequest(op, status, time.Since(start).Seconds())
	}
	return resp, err
}

func (c *Client) doInner(ctx context.Context, req Request) (*Response, error) {
	base, err := url.Parse(c.baseURL)
	if err != nil || base.Scheme == "" || base.Host == "" {
		return nil, fmt.Errorf("%w: %s", ErrInvalidBaseURL, c.baseURL)
	}
	if !strings.HasPrefix(req.Path, "/") {
		return nil, fmt.Errorf("cloud runtime path must start with /: %s", req.Path)
	}

	u := *base
	u.Path = strings.TrimRight(base.Path, "/") + req.Path
	u.RawQuery = req.Query.Encode()

	var body io.Reader
	if len(req.Body) > 0 {
		body = bytes.NewReader(req.Body)
	}
	httpReq, err := http.NewRequestWithContext(ctx, req.Method, u.String(), body)
	if err != nil {
		return nil, err
	}
	httpReq.Header.Set("Accept", "application/json")

View on GitHub (pinned to 2c0912b6ec)

Solutions

  1. Set a complete base URL including scheme and host, e.g. 'https://runtime.example.com'
  2. Validate the URL once at startup (url.Parse + scheme/host check) and fail configuration, not per-request
  3. Check where the client's baseURL comes from (env/config flag) and confirm the value actually reaches the constructor

Example fix

// before
base, err := url.Parse(c.baseURL)
if err != nil || base.Scheme == "" || base.Host == "" {
    return nil, fmt.Errorf("%w: %s", ErrInvalidBaseURL, c.baseURL)
}

// after (caller side): validate at construction so requests never carry a bad URL
if _, err := url.Parse(baseURL); err != nil {
    return nil, fmt.Errorf("base URL: %w", err)
}
u, _ := url.Parse(baseURL)
if u.Scheme == "" || u.Host == "" {
    return nil, fmt.Errorf("base URL %q must include scheme and host", baseURL)
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate before constructing/using the client.
func ValidBaseURL(raw string) bool {
    u, err := url.Parse(strings.TrimSpace(raw))
    return err == nil && u.Scheme != "" && u.Host != ""
}

if !ValidBaseURL(cfg.CloudRuntimeURL) {
    return fmt.Errorf("cloud runtime URL %q must include scheme and host", cfg.CloudRuntimeURL)
}

Try / catch

Check errors.Is(err, cloudruntime.ErrInvalidBaseURL) and route to configuration-error handling (fail fast at startup) rather than generic request-failure handling.

Prevention

When it happens

Trigger: Constructing or calling the cloud runtime client with baseURL set to '', 'localhost:8080' (no scheme), 'http://' (no host), or a value with embedded whitespace/control characters. Also a baseURL sourced from an env var or server config that was never validated.

Common situations: MULTICA_* env var typos, config files with empty/placeholder URLs ('TODO'), URLs copied with a leading space, or a URL built by string concatenation that dropped the scheme.

Related errors


AI-assisted analysis of multica-ai/multica@2c0912b6ec (2026-08-15). Data as JSON: /api/errors/d25da9f586da05d8. Report an issue: GitHub.