@Mail Domain Check

Keycloak 26.7.0 | Verify email | Restart boundary

Keycloak Verification Link Returns 500 After Restart

If the email arrived but its link fails only after Keycloak restarts and it is opened in an incognito window, SMTP and inbox delivery already passed. The source-level boundary is the original root authentication session used by the cross-browser confirmation flow.

Fastest source check

In VerifyEmailActionTokenHandler, inspect the result of getRootAuthenticationSession before it is passed to removeRootAuthenticationSession. After restart, the default provider can return null.

This is after email delivery

Email received
The application, mail transport, provider and receiving mailbox completed enough work to expose the verification URL. Changing SPF, DKIM, SMTP credentials or inbox rules cannot repair a server-side 500 on link use.
Fresh browser
With no matching browser authentication session, Keycloak creates a fresh session, embeds the original compound session ID into the confirmation action token and asks the user to confirm.
Restart
The default Infinispan authentication-session provider can no longer return the original root session after process state is lost. The handler must tolerate that cleanup target already being absent.

Browser-local routing tool

Locate the Keycloak restart boundary

No verification URL, action token, user ID, email address, realm, session ID, cookie or server log is requested or transmitted.

What Keycloak 26.7.0 source shows

VerifyEmailActionToken extends DefaultActionToken and carries the compound authentication-session ID, email and client context in the signed action token. The reported path is therefore not proved to be an actionTokens cache lookup failure.

When the request needs a fresh authentication session, VerifyEmailActionTokenHandler copies the original compound session ID into oasid, writes the fresh session ID into the token and renders a confirmation URL.

After confirmation, the handler verifies the user and then reads the original root session. In 26.7.0 it passes that result directly to removeRootAuthenticationSession. The default Infinispan provider can return null for a missing session, while its remove method dereferences the model immediately. That is the concrete null boundary to test.

A minimal defensive correction is to remove the original root session only when the lookup returns a model. Keep signature, expiry, realm, client, email and required-action checks unchanged. A missing cleanup target is not permission to accept an invalid token.

Why stateless can change the result

Keycloak 26.7.0 registers JpaAuthenticationSessionProviderFactory only when the STATELESS feature is enabled. That moves authentication sessions to the database, so the original root session can survive a process restart.

The 26.7.0 release notes describe stateless as part of the preview multi-cluster v2 architecture. Treat it as a topology decision with its own upgrade and load testing, not as a substitute for the null-safe handler fix in every deployment.

Run one bounded four-case control

  1. Create a controlled user and issue a verification email. Store no action URL in shared logs.
  2. Test the original browser without a restart and a fresh browser without a restart.
  3. Repeat each browser case after one deliberate Keycloak restart.
  4. For every case, record token validation, confirmation rendering, user verification, required-action removal and original-session cleanup separately.
  5. If the original root lookup is absent, prove the null guard skips only cleanup and still returns the normal verified result.

Regression contract

  1. Issue one valid verify-email action token tied to an authentication session.
  2. Simulate the original root session disappearing before a fresh-browser confirmation.
  3. Assert the user is verified exactly once, the required action is removed and the response is not 500.
  4. Assert an existing original session is still removed in the normal cross-browser case.
  5. Assert expired, malformed, wrong-client and replayed tokens keep their existing rejection behavior.
  6. Assert a post-verification rendering or cleanup failure never causes a duplicate verification message to be issued automatically.
Different failure state?

Build a local incident plan across action-token validation, application state, provider delivery and receiver evidence without entering a link or account identifier.

Build verification plan

Primary references