{"record":{"id":"25bf62839f6e3a1f","repo":"apereo/cas","slug":"invalid-request-25bf62","errorCode":"invalid_request","errorMessage":"The presentation response could not be validated","messagePattern":"The presentation response could not be validated","errorType":"console","errorClass":null,"httpStatus":400,"severity":"error","filePath":"support/cas-server-support-oidc-vc/src/main/java/org/apereo/cas/vc/presentation/OidcVerifiableCredentialPresentationResponseEndpointController.java","lineNumber":111,"sourceCode":"        @RequestParam final String state) {\n\n        try {\n            require(!vpToken.isBlank() && !state.isBlank(), \"Presentation response parameters cannot be blank\");\n            val transientSessionTicket = configurationContext.getTicketRegistry().getTicket(state, TransientSessionTicket.class);\n            require(transientSessionTicket != null && !transientSessionTicket.isExpired(), \"Presentation transaction is invalid\");\n            require(state.equals(transientSessionTicket.getPropertyAsString(\"state\")), \"Presentation state does not match\");\n\n            val nonce = transientSessionTicket.getPropertyAsString(\"nonce\");\n            require(nonce != null && !nonce.isBlank(), \"Presentation transaction has no nonce\");\n            val credentials = (List<CredentialRequest>) transientSessionTicket.getProperty(\"credentials\", List.class);\n            require(credentials != null && !credentials.isEmpty(), \"Presentation transaction has no credential query\");\n\n            validatePresentation(vpToken, credentials, nonce, transientSessionTicket);\n            configurationContext.getTicketRegistry().deleteTicket(transientSessionTicket);\n            \n            return buildResponse(HttpStatus.OK, Map.of(\"status\", \"verified\"));\n        } catch (final Throwable throwable) {\n            LoggingUtils.warn(LOGGER, throwable);\n            return buildResponse(HttpStatus.BAD_REQUEST,\n                OAuth20Utils.getErrorResponseBody(OAuth20Constants.INVALID_REQUEST,\n                    \"The presentation response could not be validated\")\n            );\n        }\n    }\n\n    private void validatePresentation(final String vpToken,\n                                      final List<CredentialRequest> credentials,\n                                      final String nonce,\n                                      final TransientSessionTicket transientSessionTicket) throws Throwable {\n        val credentialQueries = new LinkedHashMap<String, CredentialRequest>();\n        for (val credential : credentials) {\n            require(credential != null && credential.getId() != null && !credential.getId().isBlank(),\n                \"Credential query id is invalid\");\n            require(OidcVerifiableCredentialConfigurationProperties.CredentialConfigurationFormats.DC_SD_JWT\n                .getValue().equals(credential.getFormat()), \"Credential query format is not supported\");\n            require(credential.getVctValues() != null && !credential.getVctValues().isEmpty()","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oidc-vc/src/main/java/org/apereo/cas/vc/presentation/OidcVerifiableCredentialPresentationResponseEndpointController.java#L93-L129","documentation":"OidcVerifiableCredentialPresentationResponseEndpointController.handleResponse validates a verifiable presentation submission (vp_token, credentials, nonce, transient session ticket) inside a try block. Any Throwable during validation results in HTTP 400 with error 'invalid_request' and message 'The presentation response could not be validated'; the transient session ticket is only deleted on success.","triggerScenarios":"Submitting a presentation response whose vp_token signature/proof fails verification, credentials don't match the requested types, the nonce doesn't match the issued one, or the transient session ticket is missing/expired — any exception in validatePresentation.","commonSituations":"Wallet replaying an old nonce; clock skew invalidating credential validity; unsupported proof/algorithm in the holder's presentation; user retrying after the transient ticket was already consumed or expired.","solutions":["Re-run the authorization/presentation flow to get a fresh nonce and transient session ticket, then submit the response once.","Verify the holder's presentation is signed with a supported algorithm and the credentials match the requested types.","Ensure the nonce from the earlier response is included unchanged in the presentation.","Inspect CAS logs (LoggingUtils warns with the underlying throwable) to identify the exact validation failure."],"exampleFix":"// before: reusing a consumed ticket/nonce\nPOST presentation-response with old vp_token + old nonce\n// after: restart flow\n1) obtain new nonce + transient ticket\n2) build fresh vp_token including that nonce\n3) POST once","handlingStrategy":"try-catch","validationCode":"// Client-side sanity checks before submitting the presentation\nif (vpToken == null || nonce == null || transientTicketId == null) {\n    throw new IllegalArgumentException(\"vp_token, nonce and transient ticket are required\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    Response resp = submitPresentation(vpToken, credentials, nonce, ticketId);\n    if (resp.status() == 400 && body.contains(\"could not be validated\")) {\n        restartFlow(); // new nonce + ticket, fresh presentation\n    }\n} catch (IOException e) {\n    LOGGER.warn(\"Presentation submission failed\", e);\n}","preventionTips":["Submit the presentation response exactly once; tickets are single-use","Include the issued nonce verbatim in the presentation","Keep holder keys and algorithms within CAS-supported set","Restart the flow on any 400 rather than retrying the same payload"],"tags":["oidc","verifiable-credentials","presentation","validation"],"backgroundTag":"schema-validation-failed","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"}