semaphoreui/semaphore · error

OIDC sign-in failed: invalid state. Try signing in again.

Error message

OIDC sign-in failed: invalid state. Try signing in again.

What it means

In oidcRedirect, the 'state' query parameter from the IdP callback is base64-URL-decoded before JSON-unmarshaling into oAuthState. If base64.URLEncoding.DecodeString fails (the state is not valid base64url), the handler logs the error and returns HTTP 400 'OIDC sign-in failed: invalid state. Try signing in again.' The state is treated as corrupt and the flow is aborted.

Solutions

  1. Restart the OIDC sign-in flow to get a fresh, intact state
  2. Check for proxies/gateways that rewrite query strings and fix their encoding handling
  3. Ensure the IdP returns the state parameter unmodified (compare with the issued authorize URL)
  4. Have users open the callback link directly rather than copy/pasting through tools that alter it
Defensive patterns

Strategy: retry

Try / catch

// client-side: if the IdP lands on the callback with a mangled state, restart the flow
if (new URLSearchParams(location.search).get('state') === null || /[^A-Za-z0-9_-]/.test(state)) {
    window.location.href = '/api/auth/oidc/' + pid
}

Prevention

When it happens

Trigger: IdP redirects back with a state query parameter that is not valid base64url (truncated, URL-encoded/modified, or fabricated); middleware or proxy rewriting the query string; manually constructed callback URL.

Common situations: State parameter mangled by double URL-encoding in a proxy; user editing/bookmarking the callback URL and cutting off characters; non-compliant IdP altering the state; copying the URL through a chat that wrapped/broke it.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07). Data as JSON: /api/errors/79783cf147397156. Report an issue: GitHub.

Appendix: source

Thrown at api/login.go:864

	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)
		return
	}

	if stateData.Csrf != oauthState.Value {
		http.Error(w, "OIDC sign-in failed: state mismatch. Try signing in again.", http.StatusBadRequest)
		return
	}

	ctx := context.Background()

View on GitHub (pinned to 1774ccb71a)