semaphoreui/semaphore · error
OIDC sign-in failed: state cookie is missing. Try signing…
Error message
OIDC sign-in failed: state cookie is missing. Try signing in again.
What it means
oidcRedirect is the OIDC callback handler. Before validating the OAuth state it reads the signed state cookie that oidcLogin set earlier (via generateStateOauthCookie). If reading/decoding that cookie fails (err != nil at this point), it responds HTTP 400 'OIDC sign-in failed: state cookie is missing. Try signing in again.' This protects against state-forgery since the cookie carries the signed CSRF value.
Solutions
- Restart the sign-in flow from the beginning (click login again) so a fresh state cookie is set
- Enable cookies for the Semaphore domain and don't clear them mid-login
- Ensure the callback URL uses the same domain/scheme that set the cookie (behind proxy, forward cookies and set correct Cookie domain/Secure settings)
- Complete the sign-in promptly to avoid state-cookie expiry
Defensive patterns
Strategy: retry
Validate before calling
// detect that a login flow was actually started (state cookie exists) before loading callback URLs:
if (document.cookie.indexOf('oauth_state') === -1) { /* restart login from /api/auth/oidc/<pid> */ } Try / catch
// on the login page, if callback reports missing state cookie:
if (location.search.includes('error=state_cookie_missing')) {
window.location.href = '/api/auth/oidc/' + pid // full restart of the flow
} Prevention
- Complete sign-in in one browser/tab without long pauses (cookie is short-lived)
- Don't block or clear cookies for the Semaphore domain during login
- Ensure IdP redirect URIs use the same domain that set the state cookie
- Configure proxies to pass Set-Cookie/Cookie headers untouched
When it happens
Trigger: Redirecting from the IdP back to the callback URL with no 'state' (oauth state) cookie present in the request — cookie expired, deleted, blocked, or the flow was started in a different browser; calling the callback URL directly without going through oidcLogin.
Common situations: User waiting too long so the short-lived state cookie expired; browser blocking third-party/same-site cookies; IdP redirecting to a different domain than the one that set the cookie; reverse proxy stripping cookies; user copying the callback URL into another browser.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Account linking must be initiated with a POST request.
- You must be signed in to link an external account.
- OIDC sign-in failed: invalid state. Try signing in again.
- OIDC sign-in failed: state mismatch. Try signing in again.
- Unauthorized
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/7713676e30eb95ed.
Report an issue: GitHub.
Appendix: source
Thrown at api/login.go:855
if webHost == "" {
return redirectPath, nil
}
return url.JoinPath(webHost, redirectPath)
}
func oidcRedirect(w http.ResponseWriter, r *http.Request) {
pid := mux.Vars(r)["provider"]
oauthState, err := r.Cookie("oauthstate")
// Errors are shown as plain text at the current URL instead of a silent
// redirect to the login page, so the user can see what went wrong.
// Details stay in server logs.
if err != nil {
log.Error(err.Error())
http.Error(w, "OIDC sign-in failed: state cookie is missing. Try signing in again.", http.StatusBadRequest)
return
}
s := r.FormValue("state")
b, err := base64.URLEncoding.DecodeString(s)
if err != nil {
log.Error(err.Error())
http.Error(w, "OIDC sign-in failed: invalid state. Try signing in again.", http.StatusBadRequest)
return
}
var stateData oAuthState
err = json.Unmarshal(b, &stateData)
if err != nil {
log.Error(err.Error())
http.Error(w, "OIDC sign-in failed: invalid state. Try signing in again.", http.StatusBadRequest)View on GitHub (pinned to 1774ccb71a)