bytebase/bytebase · error

%s must not carry a query string

Error message

%s must not carry a query string

What it means

NormalizeExternalURL rejects URLs that contain a query string (RawQuery non-empty or ForceQuery set, i.e. a trailing "?"). The external URL is used as an OAuth redirect base, so a query component would corrupt redirect URLs.

Source

Thrown at backend/common/util.go:142

func NormalizeExternalURL(externalURL string) (string, error) {
	r := strings.TrimSpace(externalURL)
	r = strings.TrimSuffix(r, "/")
	u, err := url.Parse(r)
	if err != nil {
		return "", errors.Wrapf(err, "%s malformed", externalURL)
	}
	scheme := strings.ToLower(u.Scheme)
	if scheme != "http" && scheme != "https" {
		return "", errors.Errorf("%s must start with http:// or https://", externalURL)
	}
	if u.Host == "" {
		return "", errors.Errorf("%s must name a host", externalURL)
	}
	if u.User != nil {
		return "", errors.Errorf("%s must not carry userinfo", externalURL)
	}
	if u.RawQuery != "" || u.ForceQuery {
		return "", errors.Errorf("%s must not carry a query string", externalURL)
	}
	if u.Fragment != "" || u.RawFragment != "" {
		return "", errors.Errorf("%s must not carry a fragment", externalURL)
	}

	host := strings.ToLower(u.Host)
	port := u.Port()
	if port != "" {
		// The external URL is used as the redirectURL in the get token process of OAuth, and the
		// RedirectURL needs to be consistent with the RedirectURL in the get code process.
		// The frontend gets it through window.location.origin in the get code
		// process, so port 80/443 need to be cropped.
		if (scheme == "http" && port == "80") || (scheme == "https" && port == "443") {
			host = strings.ToLower(u.Hostname())
			if strings.Contains(host, ":") {
				host = "[" + host + "]"
			}
		}

View on GitHub (pinned to 1870550677)

Solutions

  1. Strip everything from "?" onward; configure only scheme://host[:port][/path].
  2. Copy the origin from the browser address bar up to (but not including) the "?".
  3. Re-run the workspace settings validation after cleaning the URL.

Example fix

// before
NormalizeExternalURL("https://example.com/?utm_source=x")
// after
NormalizeExternalURL("https://example.com")
Defensive patterns

Strategy: validation

Validate before calling

u, _ := url.Parse(strings.TrimSpace(externalURL))
if u.RawQuery != "" || u.ForceQuery {
  return errors.New("external URL must not contain a query string; strip everything after '?'")
}

Try / catch

normalized, err := common.NormalizeExternalURL(u)
if err != nil && strings.Contains(err.Error(), "must not carry a query string") {
  if cleaned := strings.SplitN(u, "?", 2)[0]; cleaned != "" {
    normalized, err = common.NormalizeExternalURL(cleaned)
  }
}

Prevention

When it happens

Trigger: Calling NormalizeExternalURL with "https://example.com/?utm_source=x" or "https://example.com/?"; reached via the workspace external URL validation call sites.

Common situations: Copying a full page URL including tracking parameters (utm_*, session IDs) instead of the bare origin.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06). Data as JSON: /api/errors/ae1ee5a09b977657. Report an issue: GitHub.