apereo/cas · error · IllegalArgumentException

subordinate directory

Error message

subordinate directory [%s] does not exist

What it means

During repository initialization, loadSubordinates() validates the configured subordinate directory before scanning it for OidcFederationSubordinate JSON files. If the path is non-blank but Files.exists() reports no such path, it throws this IllegalArgumentException to fail fast with the offending path in the message.

Solutions

  1. Create the directory at the configured path: mkdir -p <subordinateDirectory> and place subordinate JSON files in it
  2. Correct the cas.oidc.federation subordinate-directory property to an existing absolute path
  3. In containers, mount the directory into the container at the exact configured path and restart CAS

Example fix

// before
cas.oidc.federation.subordinate-directory=/etc/cas/oidc/subordinates
// directory missing -> error
// after
# on host
mkdir -p /etc/cas/oidc/subordinates
# place *.json subordinate entity files there, then restart CAS
Defensive patterns

Strategy: validation

Validate before calling

val dir = Paths.get(subordinateDirectory);
if (StringUtils.isBlank(subordinateDirectory) || !Files.exists(dir) || !Files.isDirectory(dir)) {
    throw new IllegalStateException("Subordinate directory not usable: " + subordinateDirectory);
}

Try / catch

try {
    repository = new OidcFederationSubordinateRepository(oidcProperties, ...);
} catch (IllegalArgumentException e) {
    LOGGER.error("Subordinate dir missing: {}", e.getMessage());
}

Prevention

When it happens

Trigger: CAS starts with cas.oidc.federation.subordinate-directory (or equivalent bean argument) pointing to a path that does not exist on disk, e.g. a typo, an unmounted volume, or a container path not mounted into the CAS server.

Common situations: Docker/Kubernetes deployments where the subordinate JSON directory is not mounted or mounted at a different path; developer copied a config from another host; directory created after CAS startup with no restart; relative path resolved against an unexpected working directory.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/16d70f73484c029f. 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:42

@Slf4j
public class OidcFederationSubordinateRepository {

    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)