apereo/cas · warning
Grouper WS did not respond successfully. Ensure your…
Error message
Grouper WS did not respond successfully. Ensure your credentials are correct , the url endpoint for Grouper WS is correctly configured and the subject [{}] exists in Grouper. What it means
This is a warning logged by DefaultGrouperFacade.getGroupsForSubjectId when the Grouper Web Service call (WsGetGroupsRequest) throws any exception. CAS could not retrieve groups for the subject from the Grouper WS endpoint, so it returns an empty list instead of the group set. The catch-all means any failure — connectivity, authentication, bad subject id, malformed response — surfaces as this single message.
Solutions
- Verify the Grouper WS URL endpoint and credentials in the CAS grouper configuration properties and test them with curl against the WS endpoint.
- Confirm the subject id exists in Grouper by querying the WS directly (WsGetSubjects / getGroups) for that subject.
- Read the attached exception `e` in the same log line — it identifies the real cause (connectivity vs auth vs parse).
- Check network reachability (DNS, firewall, TLS trust store) from the CAS host to the Grouper WS server.
Example fix
// before cas.authn.grouper.url=https://grouper.example.edu/grouper-ws/servicesRest // after cas.authn.grouper.url=https://grouper.example.edu:443/grouper-ws/v2_5_000/servicesRest // and verify with: // curl -u wsUser:wsPass -X POST https://grouper.example.edu/grouper-ws/v2_5_000/servicesRest/v2_5_000/groups
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check config and reachability before calling the facade val wsUrl = casProperties.getAuthn().getGrouper().getWsUrl(); Assert.hasText(wsUrl, "Grouper WS URL must be configured"); var conn = new URL(wsUrl).openConnection(); conn.setConnectTimeout(3000); conn.connect(); // throws earlier with a clearer error if endpoint unreachable
Try / catch
try {
var groups = facade.getGroupsForSubjectId(subjectId);
if (groups.isEmpty()) {
// degrade gracefully: no groups != fatal
logger.warn("Grouper returned no groups for {}; continuing with defaults", subjectId);
}
} catch (Exception e) {
logger.error("Grouper WS unreachable for subject {}", subjectId, e);
} Prevention
- Health-check the Grouper WS endpoint at startup or via actuator.
- Keep WS credentials in a secret store, not plaintext config, and rotate with config refresh.
- Log and monitor the nested exception in the same log line — it names the real cause.
- Pin and test the Grouper WS API version explicitly in the URL.
When it happens
Trigger: Calling getGroupsForSubjectId(subjectId) when: the Grouper WS URL is wrong or unreachable, the configured WS credentials (user/password) are rejected, the subject id does not exist in the Grouper subject source, or the WS client throws while parsing the response (e.g. WsGetGroupsResult unavailable).
Common situations: cas.authn.grouper / GrouperGroupField configuration pointing to a wrong host or port; Grouper WS credentials rotated without updating CAS; LDAP/subject source ids mismatched so the subject lookup fails; network/firewall blocking the WS endpoint; TLS certificate issues against the Grouper server.
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
- not in allowed range.
- Authentication handler is disabled
- No user can be accepted because none is defined
- not found in backing map.
- Unable to authenticate
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/5d972e09f1da1f09.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-grouper-core/src/main/java/org/apereo/cas/grouper/DefaultGrouperFacade.java:36
*
* @author Misagh Moayyed
* @since 5.1.0
*/
@Slf4j
public class DefaultGrouperFacade implements GrouperFacade {
@Override
public Collection<WsGetGroupsResult> getGroupsForSubjectId(final String subjectId) {
try {
val results = fetchGroupsFor(subjectId);
if (results == null || results.length == 0) {
LOGGER.warn("Subject id [{}] could not be located.", subjectId);
return new ArrayList<>();
}
LOGGER.debug("Found [{}] groups for [{}]", results.length, subjectId);
return CollectionUtils.wrapList(results);
} catch (final Exception e) {
LOGGER.warn("Grouper WS did not respond successfully. Ensure your credentials are correct "
+ ", the url endpoint for Grouper WS is correctly configured and the subject [{}] exists in Grouper.", subjectId, e);
}
return new ArrayList<>();
}
protected WsGetGroupsResult[] fetchGroupsFor(final String subjectId) {
val groupsClient = new GcGetGroups().addSubjectId(subjectId);
return groupsClient.execute().getResults();
}
@Override
public WsGetPermissionAssignmentsResults getPermissionAssignments(final GrouperPermissionAssignmentsQuery query) {
val gcGetPermissionAssignments = new GcGetPermissionAssignments();
FunctionUtils.doIfNotBlank(query.getAttributeDefinitionName(), gcGetPermissionAssignments::addAttributeDefName);
FunctionUtils.doIfNotBlank(query.getRoleName(), gcGetPermissionAssignments::addAttributeDefName);
FunctionUtils.doIfNotBlank(query.getRoleUuid(), gcGetPermissionAssignments::addRoleUuid);
FunctionUtils.doIfNotBlank(query.getSubjectAttributeName(), gcGetPermissionAssignments::addSubjectAttributeName);
View on GitHub (pinned to e7288fc434)