Wei-Shaw/sub2api · error

base URL must not include a fragment

Error message

base URL must not include a fragment

What it means

Thrown by normalizeKnownBaseURLPath when the supplied base URL has a URL fragment ('#...'). Fragments are purely client-side and meaningless in an API base URL, so their presence is treated as a misconfigured value rather than silently ignored.

Source

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

// 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
}

// IsOfficialBaseURLHost 报告 host 是否属于官方 API / 区域 API / CLI 网关主机。
func IsOfficialBaseURLHost(host string) bool {
	host = strings.ToLower(strings.TrimSpace(host))

View on GitHub (pinned to 073e92d171)

Solutions

  1. Strip the fragment from the base URL ('https://api.x.ai/v1#auth' -> 'https://api.x.ai/v1').
  2. Copy the endpoint from the API's raw endpoint list rather than the address bar of a docs page with anchors.
  3. Add a pre-save lint in your config layer that rejects or trims '#'.

Example fix

// before
baseURL := "https://api.x.ai/v1#authentication"

// after
baseURL := "https://api.x.ai/v1"
Defensive patterns

Strategy: validation

Validate before calling

func stripFragment(raw string) string {
    if i := strings.Index(raw, "#"); i >= 0 {
        return strings.TrimRight(raw[:i], "/")
    }
    return strings.TrimRight(raw, "/")
}

Try / catch

if _, err := xai.NormalizeBaseURL(input); err != nil {
    if strings.Contains(err.Error(), "fragment") {
        input = input[:strings.Index(input, "#")] // retry once with fragment stripped
    }
}

Prevention

When it happens

Trigger: Passing a base URL like 'https://api.x.ai/v1#section' or 'https://api.x.ai/#' through normalizeKnownBaseURLPath. Parse succeeds but parsed.Fragment is non-empty.

Common situations: Base URL copy-pasted from documentation pages where the docs site appends an anchor (#authentication, #rate-limits); users manually trimming a URL but leaving a trailing '#'; template concatenation bugs that glue an anchor onto the endpoint.

Related errors


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