argoproj/argo-workflows · error

logout redirect URL must be an absolute HTTP(S) URL without

Error message

logout redirect URL must be an absolute HTTP(S) URL without user info or a fragment: %q

What it means

ValidateRedirectURL checks the operator-supplied post-logout redirect URL configured on the Argo Server. It rejects any non-empty value that is not an absolute HTTP(S) URL (the shared parseAbsoluteHTTPURL helper also excludes user info and fragments). This guards against malformed or unsafe logout redirects.

Source

Thrown at server/logout/logout.go:40

// If the provider end-session URL is invalid, the handler falls back to the local redirect and returns the validation error.
func NewHandler(baseHRef, redirectURL string, secure bool, logoutURL, clientID string) (*Handler, error) {
	baseHRef = authcookie.NormalizePath(baseHRef)
	cookiePaths := []string{baseHRef}
	if legacyCookiePath := strings.TrimSuffix(baseHRef, "/"); legacyCookiePath != "" {
		cookiePaths = append(cookiePaths, legacyCookiePath)
	}
	if redirectURL == "" {
		redirectURL = baseHRef
		logoutURL = ""
	}
	finalRedirectURL, err := constructLogoutURL(logoutURL, clientID, redirectURL)
	return &Handler{cookiePaths: cookiePaths, redirectURL: finalRedirectURL, secure: secure}, err
}

// ValidateRedirectURL validates the optional post-logout redirect URL supplied by an operator.
func ValidateRedirectURL(redirectURL string) error {
	if redirectURL != "" && !isAbsoluteHTTPURL(redirectURL) {
		return fmt.Errorf("logout redirect URL must be an absolute HTTP(S) URL without user info or a fragment: %q", redirectURL)
	}
	return nil
}

func isAbsoluteHTTPURL(rawURL string) bool {
	_, ok := parseAbsoluteHTTPURL(rawURL)
	return ok
}

func parseAbsoluteHTTPURL(rawURL string) (*url.URL, bool) {
	parsedURL, err := url.Parse(rawURL)
	if err != nil || parsedURL.Hostname() == "" || parsedURL.User != nil || parsedURL.Fragment != "" ||
		(!strings.EqualFold(parsedURL.Scheme, "http") && !strings.EqualFold(parsedURL.Scheme, "https")) {
		return nil, false
	}
	return parsedURL, true
}

View on GitHub (pinned to 35bff19146)

Solutions

  1. Set the URL to a fully qualified absolute URL starting with http:// or https://
  2. Remove any #fragment and user:password@ userinfo from the URL
  3. If no post-logout redirect is desired, leave the value empty (empty is valid)

Example fix

// before
ARGO_SERVER_REDIRECT_URL_AFTER_LOGOUT=/goodbye
// after
ARGO_SERVER_REDIRECT_URL_AFTER_LOGOUT=https://sso.example.com/logout
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(cfg.LogoutRedirectURL)
if cfg.LogoutRedirectURL != "" && (err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" || u.User != nil || u.Fragment != "") {
    return fmt.Errorf("invalid logout redirect URL: %q", cfg.LogoutRedirectURL)
}

Type guard

func isSafeAbsoluteURL(raw string) bool {
    u, err := url.Parse(raw)
    return err == nil && raw != "" && (u.Scheme == "http" || u.Scheme == "https") && u.Host != "" && u.User == nil && u.Fragment == ""
}

Try / catch

if err := logout.ValidateRedirectURL(cfg.LogoutRedirectURL); err != nil {
    log.Fatalf("bad logout redirect URL: %v", err)
}

Prevention

When it happens

Trigger: Starting `argo server` with a logout redirect URL (e.g. via --redirect-url-after-logout or env var) that is relative ("/logged-out"), not http(s) ("ftp://x"), contains userinfo ("https://user:pass@host") or a fragment ("https://host#frag").

Common situations: Operators copying OIDC provider logout URLs with fragments, mistyping the flag, or setting a relative path believing relative redirects are allowed.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/5102fa6258f7e0c7. Report an issue: GitHub.