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
- Remove the query string from the configured base URL (keep scheme://host/path only).
- Move any intended parameter (e.g. an API key) into the header/auth field it belongs to, not the base URL.
- 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
- Strip everything from the first '?' before storing a base URL
- Validate base URLs in config-load tests
- Keep auth params in headers, never in the base URL
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
- base URL must not include a fragment
- dingtalk: internal_only requires app_type=internal
- token_exchange_failed
- userinfo_failed
- auth.dingtalk.callbackMissingToken
AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15).
Data as JSON: /api/errors/1316d4c7416bf4ba.
Report an issue: GitHub.