Wei-Shaw/sub2api · error

base URL must not include a query

Error message

base URL must not include a query

What it means

Thrown by normalizeKnownBaseURLPath in the xAI OAuth package when the configured base URL contains a query string (either a bare '?' via ForceQuery or actual RawQuery). Base URLs for this package must be scheme + host + optional path only; query strings are rejected outright as a misconfiguration guard.

Source

Thrown at backend/internal/pkg/xai/oauth.go:434

	return normalizeKnownBaseURLPath(normalized)
}

// normalizeKnownBaseURLPath 规范化 base URL 的 path 部分:
//   - 官方主机固定使用 /v1 前缀(空 path 自动补齐,其余 path 拒绝);
//   - 其他主机保留管理员配置的任意 path 前缀(第三方转发地址常见
//     /xxx/v1 之类的路由前缀),空 path 仍按惯例补 /v1。
//
// 所有主机统一禁止 userinfo/query/fragment,并去除尾部斜杠。
func normalizeKnownBaseURLPath(raw string) (string, error) {
	parsed, err := url.Parse(raw)
	if err != nil || parsed.Scheme == "" || parsed.Host == "" {
		return "", errors.New("invalid base URL")
	}
	if parsed.User != nil {
		return "", errors.New("base URL must not include userinfo")
	}
	if parsed.ForceQuery || parsed.RawQuery != "" {
		return "", errors.New("base URL must not include a query")
	}
	if parsed.Fragment != "" {
		return "", errors.New("base URL must not include a fragment")
	}
	path := strings.TrimRight(parsed.Path, "/")
	if path == "" {
		parsed.Path = "/v1"
		parsed.RawPath = ""
		return strings.TrimRight(parsed.String(), "/"), nil
	}
	if path != "/v1" && IsOfficialBaseURLHost(parsed.Hostname()) {
		return "", fmt.Errorf("base URL path must be /v1")
	}
	parsed.Path = path
	parsed.RawPath = ""
	return strings.TrimRight(parsed.String(), "/"), nil
}

View on GitHub (pinned to 073e92d171)

Solutions

  1. Remove the query string from the configured base URL (keep scheme://host/path only).
  2. Move any intended parameter (e.g. an API key) into the header/auth field it belongs to, not the base URL.
  3. Sanitize stored configs: strip anything from the first '?' before persisting or calling the normalizer.

Example fix

// before
baseURL := "https://api.x.ai/v1?version=2024-01-01"

// after
baseURL := "https://api.x.ai/v1" // version belongs in headers
Defensive patterns

Strategy: validation

Validate before calling

func sanitizeBaseURL(raw string) (string, error) {
    u, err := url.Parse(raw)
    if err != nil || u.Scheme == "" || u.Host == "" {
        return "", fmt.Errorf("invalid base URL %q", raw)
    }
    u.ForceQuery = false
    u.RawQuery = ""
    u.Fragment = ""
    return strings.TrimRight(u.String(), "/"), nil
}

Try / catch

normalized, err := xai.NormalizeBaseURL(input)
if err != nil {
    return fmt.Errorf("please check the xAI base URL config (no query strings allowed): %w", err)
}

Prevention

When it happens

Trigger: Calling any API that funnels user-provided base URLs through normalizeKnownBaseURLPath with input like 'https://api.x.ai/v1?foo=bar' or 'https://api.x.ai?'. The url.Parse succeeds, scheme and host are present, but ForceQuery/RawQuery is set.

Common situations: Users paste an API endpoint copied from a browser or docs page that includes '?key=...' or tracking params; configuration UIs that append query params for defaults; env vars containing a trailing '?' after template substitution.

Related errors


AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15). Data as JSON: /api/errors/1316d4c7416bf4ba. Report an issue: GitHub.