apereo/cas · error · IllegalArgumentException
Redirect URI cannot contain a fragment
Error message
Redirect URI cannot contain a fragment
What it means
During OIDC dynamic client registration, the translator validates the registration request before creating an OidcRegisteredService. The OIDC spec forbids fragment components ('#') in redirect URIs, so any redirect URI containing '#' causes an IllegalArgumentException in translate().
Solutions
- Remove the fragment component from every redirect URI in the registration request
- Use path-based routing or a query parameter instead of a fragment for client-side routing state
- Validate redirect URIs client-side before submitting the registration request
Example fix
// before "redirect_uris": ["https://app.example.com/callback#/home"] // after "redirect_uris": ["https://app.example.com/callback?route=home"]
Defensive patterns
Strategy: validation
Validate before calling
List<String> redirectUris = request.getRedirectUris();
if (redirectUris.stream().anyMatch(u -> u.contains("#"))) {
throw new IllegalArgumentException("redirect_uris must not contain fragments");
} Type guard
boolean hasNoFragment = redirectUris.stream().allMatch(u -> u.indexOf('#') < 0); Prevention
- Strip or reject '#' in redirect URIs during client onboarding
- Use path or query parameters for SPA routing state, never fragments
- Add a pre-submit validation test for redirect URIs
When it happens
Trigger: Calling the dynamic client registration endpoint (translate in OidcDefaultClientRegistrationRequestTranslator) with a registration_request whose redirect_uris array contains at least one URI including a '#' character.
Common situations: Developers embedding a default fragment (e.g. 'https://app/callback#/home') copied from SPA frameworks, or building redirect URIs with hash-based routing; also test fixtures that accidentally include '#'.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid sector identifier uri
- Invalid logo uri from an unknown host
- Invalid policy uri from an unknown host
- Missing required principal attribute for claim
- Invalid OIDC pushed authorization request at
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/6c3576f76f5b2e1d.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/dynareg/OidcDefaultClientRegistrationRequestTranslator.java:65
private static final ObjectMapper MAPPER = JacksonObjectMapperFactory.builder()
.defaultTypingEnabled(false).build().toObjectMapper();
private static final int GENERATED_CLIENT_NAME_LENGTH = 8;
private final ObjectProvider<OidcConfigurationContext> configurationContext;
@Override
public OidcRegisteredService translate(
final OidcClientRegistrationRequest registrationRequest,
final Optional<OidcRegisteredService> givenService) throws Exception {
val context = configurationContext.getObject();
val containsFragment = registrationRequest.getRedirectUris()
.stream()
.anyMatch(uri -> uri.contains("#"));
if (containsFragment) {
throw new IllegalArgumentException("Redirect URI cannot contain a fragment");
}
val registeredService = givenService.orElseGet(OidcRegisteredService::new);
if (StringUtils.isNotBlank(registrationRequest.getClientName())) {
registeredService.setName(registrationRequest.getClientName());
} else if (StringUtils.isBlank(registeredService.getName())) {
registeredService.setName(RandomUtils.randomAlphabetic(GENERATED_CLIENT_NAME_LENGTH));
}
val serviceId = String.join("|", registrationRequest.getRedirectUris());
registeredService.setServiceId(serviceId);
registeredService.setSectorIdentifierUri(registrationRequest.getSectorIdentifierUri());
registeredService.setSubjectType(registrationRequest.getSubjectType());
if (Strings.CI.equals(OidcSubjectTypes.PAIRWISE.getType(), registeredService.getSubjectType())) {
registeredService.setUsernameAttributeProvider(new PairwiseOidcRegisteredServiceUsernameAttributeProvider());
}
View on GitHub (pinned to e7288fc434)