{"record":{"id":"45b2229ea9cca57d","repo":"apereo/cas","slug":"duo-returned-code-s-s","errorCode":null,"errorMessage":"Duo returned code %s: %s","messagePattern":"Duo returned code (.+?): (.+?)","errorType":"exception","errorClass":"DuoSecurityException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-duo-core/src/main/java/org/apereo/cas/adaptors/duo/authn/BaseDuoSecurityAuthenticationService.java","lineNumber":115,"sourceCode":"\n            if (\"OK\".equalsIgnoreCase(result.get(RESULT_KEY_STAT).asString())) {\n                val response = result.get(RESULT_KEY_RESPONSE);\n                val authResult = response.get(RESULT_KEY_RESULT).asString().toUpperCase(Locale.ENGLISH);\n\n                val status = DuoSecurityUserAccountStatus.valueOf(authResult);\n                account.setProviderId(properties.getId());\n                account.setStatus(status);\n                account.setMessage(response.get(RESULT_KEY_STATUS_MESSAGE).asString());\n                if (status == DuoSecurityUserAccountStatus.ENROLL) {\n                    val enrollUrl = response.get(RESULT_KEY_ENROLL_PORTAL_URL).asString();\n                    account.setEnrollPortalUrl(enrollUrl);\n                }\n            } else {\n                val code = result.get(RESULT_KEY_CODE).asInt();\n                if (code > RESULT_CODE_ERROR_THRESHOLD) {\n                    LOGGER.warn(\"Duo returned a failure response with code: [{}]. Duo will be considered unavailable\",\n                        result.get(RESULT_KEY_MESSAGE));\n                    throw new DuoSecurityException(\"Duo returned code %s: %s\".formatted(code, result.get(RESULT_KEY_MESSAGE)));\n                }\n                LOGGER.warn(\"Duo returned an Invalid response with message [{}] and detail [{}] \"\n                        + \"when determining user account. This maybe a configuration error in the admin request and Duo will \"\n                        + \"still be considered available.\",\n                    result.hasNonNull(RESULT_KEY_MESSAGE) ? result.get(RESULT_KEY_MESSAGE).asString() : StringUtils.EMPTY,\n                    result.hasNonNull(RESULT_KEY_MESSAGE_DETAIL) ? result.get(RESULT_KEY_MESSAGE_DETAIL).asString() : StringUtils.EMPTY);\n            }\n        } catch (final Exception e) {\n            LOGGER.warn(\"Reaching Duo has failed with error: [{}]\", e.getMessage(), e);\n            account.setStatus(DuoSecurityUserAccountStatus.UNAVAILABLE);\n        }\n\n        userAccountCachedMap.put(account.getUsername(), account);\n        LOGGER.debug(\"Fetched and cached duo user account [{}]\", account);\n        return account;\n    }\n\n    @Override","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-duo-core/src/main/java/org/apereo/cas/adaptors/duo/authn/BaseDuoSecurityAuthenticationService.java#L97-L133","documentation":"DuoSecurityException thrown by BaseDuoSecurityAuthenticationService.getUserAccount() when the Duo API responds with stat != OK and a numeric code greater than RESULT_CODE_ERROR_THRESHOLD (40000). Codes above that threshold indicate a server-side/transport-level failure, so CAS logs a warning and marks Duo as unavailable, propagating the code and message. Lower codes are treated as configuration errors and logged without failing.","triggerScenarios":"Any Duo API call from getUserAccount that returns {\"stat\":\"FAIL\",\"code\":>40000,\"message\":...} — e.g. code 40001 invalid request signature, 40101/40102 auth errors, rate-limit or server-error codes from the Duo Admin API while pre-checking the user account.","commonSituations":"Expired or wrong integration/secret keys (signature failures); Duo service outage or degraded status; Duo API rate limiting; clock skew on the CAS server breaking the signed request (date header too skewed); integration lacking permission for the admin endpoint.","solutions":["Read the Duo code and message from the log line \"Duo returned a failure response with code\" and look it up in Duo's documentation to identify the exact failure.","Regenerate/verify the integration key and secret key, and confirm the secret is not truncated or URL-encoded incorrectly in configuration.","Sync the CAS server clock (NTP) — signature-based Duo requests fail when the Date header is skewed more than 300 seconds.","Check the Duo status page (status.duosecurity.com) and retry after transient outages/rate limiting clear."],"exampleFix":"// before (skewed clock)\nDuo returned code 40101: Invalid request — expired\n\n// after: NTP-synced host + verified keys\ncas.authn.mfa.duo[0].integration-key=DIXXXXXXXXXXXXXXXXXX\ncas.authn.mfa.duo[0].secret-key=(valid skey)\n# timedatectl set-ntp true","handlingStrategy":"retry","validationCode":"// Pre-flight: call Duo /check and inspect code\nJsonNode check = duoApiCall(\"/check\");\nif (check.get(\"stat\").asText().equalsIgnoreCase(\"FAIL\") && check.get(\"code\").asInt() > 40000) {\n    throw new IllegalStateException(\"Duo unavailable, code=\" + check.get(\"code\"));\n}","typeGuard":"boolean isDuoServerFailure(JsonNode result) {\n    return result.hasNonNull(\"stat\") && \"FAIL\".equalsIgnoreCase(result.get(\"stat\").asText())\n        && result.hasNonNull(\"code\") && result.get(\"code\").asInt() > 40000;\n}","tryCatchPattern":"try {\n    account = duoService.getUserAccount(username);\n} catch (DuoSecurityException e) {\n    // transient upstream failure: retry with backoff or fail closed\n    retryWithBackoff(() -> duoService.getUserAccount(username));\n}","preventionTips":["Keep CAS host clocks NTP-synced (Duo signature tolerance is 300s).","Monitor Duo status.duosecurity.com and set alerting on code > 40000 log warnings.","Rotate and validate integration/secret keys on a schedule.","Respect Duo rate limits in batch account lookups."],"tags":["duo","mfa","api-error","upstream"],"backgroundTag":"upstream-api-error","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}