apereo/cas · error · IllegalArgumentException
subordinate directory
Error message
subordinate directory [%s] is not a directory
What it means
loadSubordinates() checks Files.isDirectory() after confirming the path exists; if the configured subordinate location resolves to a regular file (or other non-directory), it throws this IllegalArgumentException naming the path.
Solutions
- Ensure the configured path is a directory and move the subordinate JSON files into it
- Update the config to the directory path, not the individual file path
- If a single subordinate is intended, wrap it in a directory containing one JSON file
Example fix
// before subordinate-directory=/etc/cas/oidc/subordinates/acme.json # a file // after subordinate-directory=/etc/cas/oidc/subordinates # directory containing acme.json
Defensive patterns
Strategy: validation
Validate before calling
if (!Files.isDirectory(Paths.get(subordinateDirectory))) {
throw new IllegalStateException("Path is not a directory: " + subordinateDirectory);
} Prevention
- Point config at directories, never at individual JSON files
- Check symlinks resolve to a directory
- Script-provision the directory structure in deployment
When it happens
Trigger: The subordinate-directory config value points at a file (e.g. a single JSON document or an archive) instead of a directory that Files.walk() can traverse.
Common situations: Operator set the property to a JSON file expecting single-entity config; a symlink points at a file; tar extraction replaced the directory with a file.
Related errors
- subordinate directory
- Unable to build a MongoDb client without any hosts/servers…
- Cannot read/load from subordinate directory
- Invalid unique index: must be >= 0 and < 256
- Unable to create folder
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/d25163e19e4e0c6f.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-oidc-federation/src/main/java/org/apereo/cas/oidc/federation/subordinate/OidcFederationSubordinateRepository.java:45
private static final ObjectMapper MAPPER = JacksonObjectMapperFactory.builder()
.defaultTypingEnabled(false).build().toObjectMapper();
@Getter
private final Map<String, OidcFederationSubordinate> subordinates = new HashMap<>();
public OidcFederationSubordinateRepository(final OidcProperties oidcProperties) {
loadSubordinates(oidcProperties.getFederation().getSubordinateDirectory());
}
protected void loadSubordinates(final String subordinateDirectory) {
if (StringUtils.isNotBlank(subordinateDirectory)) {
LOGGER.debug("Loading subordinates...");
val dir = Paths.get(subordinateDirectory);
if (!Files.exists(dir)) {
throw new IllegalArgumentException("subordinate directory [%s] does not exist".formatted(subordinateDirectory));
}
if (!Files.isDirectory(dir)) {
throw new IllegalArgumentException("subordinate directory [%s] is not a directory".formatted(subordinateDirectory));
}
try (val stream = Files.walk(dir).filter(Files::isRegularFile).filter(Files::isReadable)) {
stream.forEach(path -> FunctionUtils.doUnchecked(_ -> {
val file = path.toFile();
LOGGER.debug("Parsing [{}]...", file);
val subordinate = MAPPER.readValue(file, OidcFederationSubordinate.class);
subordinates.put(subordinate.getEntityId(), subordinate);
}));
} catch (final IOException e) {
throw new IllegalArgumentException("Cannot read/load from subordinate directory", e);
}
LOGGER.info("Loaded [{}] subordinates", subordinates.size());
}
}
}
View on GitHub (pinned to e7288fc434)