apereo/cas · error · IllegalArgumentException
No transaction found for
Error message
No transaction found for [%s]
What it means
OidcVerifiableCredentialDefaultOfferService.fetch looks up a TransientSessionTicket by transactionId in the ticket registry and throws IllegalArgumentException when no such transaction ticket exists. It guards the credential-offer flow against unknown, expired, or consumed transaction IDs.
Solutions
- Restart the credential-offer flow to obtain a fresh transactionId
- Check the TransientSessionTicket timeout configuration and raise it if users legitimately take longer
- Verify all CAS nodes share the same ticket registry (Redis/JDBC/Hazelcast) so the ticket is visible cluster-wide
Example fix
// before OidcVerifiableCredentialOffer offer = offerService.fetch(oldTransactionId); // after String txId = startNewCredentialOfferTransaction(); OidcVerifiableCredentialOffer offer = offerService.fetch(txId); // fresh, unexpired transaction
Defensive patterns
Strategy: try-catch
Validate before calling
TransientSessionTicket t = (TransientSessionTicket) transactionService.fetch(txId);
if (t == null) { restartOfferFlow(); } Try / catch
try { return offerService.fetch(txId); }
catch (IllegalArgumentException e) { // retry with a new transaction
String newTx = startNewTransaction(); return offerService.fetch(newTx); } Prevention
- Never cache transactionIds beyond the ticket TTL
- Use a shared ticket registry in clustered deployments
- Detect expiry client-side and restart the offer flow
When it happens
Trigger: Calling fetch(transactionId) with an ID that was never issued, one whose TransientSessionTicket expired (transient tickets have a short TTL), one already consumed/removed, or a ticket registry that lost the entry (restart, non-shared registry across nodes).
Common situations: User bookmarks/refreshes an offer URL after the transient ticket expired; load-balanced CAS nodes without a shared ticket registry; client reusing an old transactionId from a previous session.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Proof JWT is too old
- [username] not found with SQL query
- JWKS cannot contain expressions
- Unable to use 'none' as introspection signing algorithm
- Unable to use 'none' as introspection encryption algorithm
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/270437235b64983f.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oidc-vc/src/main/java/org/apereo/cas/oidc/vc/offer/OidcVerifiableCredentialDefaultOfferService.java:33
* @since 8.0.0
*/
@RequiredArgsConstructor
@Slf4j
public class OidcVerifiableCredentialDefaultOfferService implements OidcVerifiableCredentialOfferService {
private final OidcConfigurationContext configurationContext;
private final OidcVerifiableCredentialTransactionService transactionService;
@Override
public OidcVerifiableCredentialOffer create(final String clientId, final String principalId, final List<String> credentialConfigurationIds) {
val transaction = (TransientSessionTicket) transactionService.issue(clientId, principalId, credentialConfigurationIds);
return buildCredentialOffer(Objects.requireNonNull(transaction));
}
@Override
public OidcVerifiableCredentialOffer fetch(final String transactionId) {
val transaction = (TransientSessionTicket) transactionService.fetch(transactionId);
if (transaction == null) {
throw new IllegalArgumentException(String.format("No transaction found for [%s]", transactionId));
}
return buildCredentialOffer(transaction);
}
private @NonNull OidcVerifiableCredentialOffer buildCredentialOffer(final TransientSessionTicket transaction) {
val credentialConfigurationIds = transaction.getProperty("credentialConfigurationIds", List.class);
val issuer = configurationContext.getCasProperties().getAuthn().getOidc().getCore().getIssuer();
val grant = new OidcVerifiableCredentialOffer.Grants.PreAuthorizedCodeGrant();
grant.setTransactionCode(
OidcVerifiableCredentialOffer.Grants.TransactionCode
.builder()
.value(Objects.requireNonNull(transaction).getId())
.length(Objects.requireNonNull(transaction).getId().length())
.build()
);
grant.setPreAuthorizedCode(transaction.getPropertyAsString("preAuthorizedCode"));
grant.setIssuerState(transaction.getPropertyAsString("issuerState"));View on GitHub (pinned to e7288fc434)