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

  1. Restart the sign-in flow from the beginning (click login again) so a fresh state cookie is set
  2. Enable cookies for the Semaphore domain and don't clear them mid-login
  3. Ensure the callback URL uses the same domain/scheme that set the cookie (behind proxy, forward cookies and set correct Cookie domain/Secure settings)
  4. 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

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


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)