semaphoreui/semaphore · error
OIDC sign-in failed: invalid redirect URL.
Error message
OIDC sign-in failed: invalid redirect URL.
What it means
oidcSuccessRedirectURL (api/login.go:835) builds the post-login redirect by joining WebHost with the redirect path ('/'+trimmed path when ReturnViaState is false, else the state-returned path). If that join returns an error (malformed WebHost / redirect path producing an invalid URL), oidcRedirect responds 500 'OIDC sign-in failed: invalid redirect URL.'
Solutions
- Check web_host in the config is a valid absolute URL (scheme + host), e.g. https://semaphore.example.com.
- Inspect the server log line for the underlying url.JoinPath error to see the offending path value.
- If ReturnViaState is enabled, verify the 'return' value stored in state is a sane relative path; sanitize it.
- Clear cookies and retry the login flow so fresh, valid state is generated.
Example fix
# before web_host: "semaphore.example.com" # no scheme -> invalid URL when joined // after web_host: "https://semaphore.example.com"
Defensive patterns
Strategy: validation
Validate before calling
// sanity-check web_host before serving login
function validWebHost(u) { try { const x = new URL(u); return x.protocol.startsWith("http"); } catch { return false; } }
if (!validWebHost(config.web_host)) alert("web_host must be an absolute http(s) URL"); Type guard
function isSafeRedirectPath(p) {
return typeof p === "string" && p.length > 0 && p.startsWith("/") && !p.startsWith("//") && !/[<>\"'\\]/.test(p);
} Try / catch
try {
await finishOidcLogin(state, code);
} catch (e) {
if (e.status === 500 && /invalid redirect URL/.test(e.body)) {
// fall back to web root and report config issue
window.location.assign("/");
}
} Prevention
- Always set web_host as a full https:// URL
- Sanitize the state 'return' value server-side to a relative path
- Add a smoke test that completes an OIDC login and asserts the redirect resolves
- Validate config at startup so malformed web_host fails fast
When it happens
Trigger: After successful auth, config.ReturnViaState routes stateData.Return, or mux var redirect_path, into oidcSuccessRedirectURL(util.Config.WebHost, redirectPath); url.JoinPath errors because web_host is malformed (e.g. contains invalid characters or unparseable URL) or the redirect path/state value is corrupt.
Common situations: web_host set to something that is not a valid absolute URL (missing scheme, stray characters); a tampered or corrupted state 'return' value; redirect_path mux variable carrying percent-encoded garbage; upgrades where web_host was left empty vs set inconsistently.
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
- OIDC sign-in failed: could not find or create the user…
- Account linking must be initiated with a POST request.
- You must be signed in to link an external account.
- OIDC sign-in failed: state cookie is missing. Try signing…
- OIDC sign-in failed: invalid state. Try signing in again.
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/600c9ce586f556b3.
Report an issue: GitHub.
Appendix: source
Thrown at api/login.go:1028
config, ok := util.Config.OidcProviders[pid]
if !ok {
log.Error(fmt.Errorf("no such provider: %s", pid))
http.Error(w, "Unknown OIDC provider.", http.StatusNotFound)
return
}
redirectPath := ""
if config.ReturnViaState {
redirectPath = stateData.Return
} else {
redirectPath = mux.Vars(r)["redirect_path"]
}
redirectURL, err := oidcSuccessRedirectURL(util.Config.WebHost, redirectPath)
if err != nil {
log.Error(err)
http.Error(w, "OIDC sign-in failed: invalid redirect URL.", http.StatusInternalServerError)
return
}
http.Redirect(w, r, redirectURL, http.StatusTemporaryRedirect)
}
View on GitHub (pinned to 1774ccb71a)