apereo/cas · error

Failed to save google authenticator account

Error message

Failed to save google authenticator account

What it means

RestGoogleAuthenticatorTokenCredentialRepository.update() posts account changes to the configured REST endpoint. When the HTTP call throws or returns an unexpected response the exception is logged and this warning is emitted, and the method returns null, meaning the save was NOT persisted.

Solutions

  1. Check the surrounding logged stack trace (LoggingUtils.error) for the root cause — connection refused vs HTTP error vs auth failure
  2. Verify cas.authn.mfa.gauth.rest.url and REST endpoint credentials are correct and the endpoint is reachable from CAS (curl it)
  3. Inspect and fix the endpoint's handling of the account payload (method, content type, serialization)
  4. Add health checks/retries on the REST service so transient outages are caught before account updates fail

Example fix

// before
cas.authn.mfa.gauth.rest.url=https://otp.internal.example.org/missingEndpoint
// after
cas.authn.mfa.gauth.rest.url=https://otp.internal.example.org/api/accounts
Defensive patterns

Strategy: retry

Validate before calling

// verify the REST endpoint before issuing account updates
boolean up = HttpUtils.areSettingsHttpsOnlyOk(...) /* or */ curl -fsS $CAS_GAUTH_REST_URL/health;

Try / catch

try {
    result = repository.save(account);
    if (result == null) { /* save silently failed — inspect logs and retry */ }
} catch (Exception e) {
    // network/endpoint failure: retry with backoff
}

Prevention

When it happens

Trigger: Calling save()/update() when the REST endpoint (cas.authn.mfa.gauth.rest.url) is down, returns a non-success status, rejects the payload, or the connection times out; exceptions inside the try block are swallowed and reduced to this warning.

Common situations: REST endpoint misconfigured or unreachable in clustered deployments; endpoint auth (basic auth credentials) missing or wrong; backend service rejects the JSON payload after an API change; network/firewall blocking inter-service calls.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/f1922f95188ea84b. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-gauth-core/src/main/java/org/apereo/cas/gauth/credential/RestGoogleAuthenticatorTokenCredentialRepository.java:254

                .method(HttpMethod.POST)
                .url(rest.getUrl())
                .headers(headers)
                .build();
            response = HttpUtils.execute(exec);

            if (response != null) {
                val status = HttpStatus.valueOf(response.getCode());
                if (status.is2xxSuccessful()) {
                    LOGGER.debug("Posted google authenticator account successfully");
                    return account;
                }
            }
        } catch (final Exception e) {
            LoggingUtils.error(LOGGER, e);
        } finally {
            HttpUtils.close(response);
        }
        LOGGER.warn("Failed to save google authenticator account");
        return null;
    }

    @Override
    public void deleteAll() {
        val rest = gauth.getRest();
        HttpResponse response = null;
        try {
            val exec = HttpExecutionRequest.builder()
                .basicAuthPassword(rest.getBasicAuthPassword())
                .basicAuthUsername(rest.getBasicAuthUsername())
                .method(HttpMethod.GET)
                .url(rest.getUrl())
                .headers(rest.getHeaders())
                .build();
            response = HttpUtils.execute(exec);
        } finally {
            HttpUtils.close(response);

View on GitHub (pinned to e7288fc434)