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
- Restart the OIDC sign-in flow to get a fresh, intact state
- Check for proxies/gateways that rewrite query strings and fix their encoding handling
- Ensure the IdP returns the state parameter unmodified (compare with the issued authorize URL)
- 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
- Never modify or truncate the callback URL
- Configure proxies/gateways to pass query strings without re-encoding
- Verify the IdP echoes the state parameter verbatim
- Avoid URL-shorteners/redirect chains between the IdP and the callback
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
- 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: state mismatch. Try signing in again.
- Unauthorized
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)