apereo/cas · warning

[ ] is not readable. Check file permissions

Error message

[{}] is not readable. Check file permissions

What it means

During load(File), AbstractResourceBasedServiceRegistry checks file.canRead() before parsing. If the file is unreadable due to OS permissions, it logs this warning and returns an empty collection — the service definition is silently not loaded.

Solutions

  1. Fix file permissions so the CAS process user can read the file (chmod/chown).
  2. Ensure files copied or mounted into the services directory are readable by all needed users (e.g. 0644).
  3. Check SELinux/AppAudit policies if permissions look correct but access is still denied.

Example fix

// before
-rw------- root root 1000001-example.json
// after
chown cas:cas 1000001-example.json && chmod 644 1000001-example.json
Defensive patterns

Strategy: validation

Validate before calling

File f = new File(path);
if (!f.canRead()) {
    throw new IllegalStateException("File not readable by CAS user: " + f.getAbsolutePath());
}

Prevention

When it happens

Trigger: Calling serviceRegistry.load(file) (directly or via directory scan/watcher) on a file the CAS process cannot read, e.g. permission bits excluding the runtime user.

Common situations: Service JSON files copied in as root with 0600 permissions; container user mismatch after volume mount; SELinux/AppArmor denials.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/27c1e7e70deeee06. Report an issue: GitHub.

Appendix: source

Thrown at core/cas-server-core-services-registry/src/main/java/org/apereo/cas/services/resource/AbstractResourceBasedServiceRegistry.java:267

                .filter(service -> StringUtils.isNotBlank(service.getServiceId()) && StringUtils.isNotBlank(service.getName()))
                .sorted()
                .collect(Collectors.toMap(RegisteredService::getId, Function.identity(),
                    (s1, s2) -> {
                        BaseResourceBasedRegisteredServiceWatcher.LOG_SERVICE_DUPLICATE.accept(s2);
                        return s1;
                    }, LinkedHashMap::new));
            val listedServices = new ArrayList<>(this.services.values());
            val results = registeredServiceReplicationStrategy.updateLoadedRegisteredServicesFromCache(listedServices, this);
            results.forEach(service -> publishEvent(new CasRegisteredServiceLoadedEvent(this, service, clientInfo)));
            return results;
        });
    }

    @Override
    public Collection<RegisteredService> load(final File file) {
        val fileName = file.getName();
        if (!file.canRead()) {
            LOGGER.warn("[{}] is not readable. Check file permissions", fileName);
            return new ArrayList<>();
        }
        if (!file.exists()) {
            LOGGER.warn("[{}] is not found at the path specified", fileName);
            return new ArrayList<>();
        }
        if (file.length() == 0) {
            LOGGER.debug("[{}] appears to be empty so no service definition will be loaded", fileName);
            return new ArrayList<>();
        }
        if (!fileName.isEmpty() && fileName.charAt(0) == '.') {
            LOGGER.debug("[{}] starts with ., ignoring", fileName);
            return new ArrayList<>();
        }
        if (Arrays.stream(getExtensions()).noneMatch(fileName::endsWith)) {
            LOGGER.debug("[{}] doesn't end with valid extension, ignoring", fileName);
            return new ArrayList<>();
        }

View on GitHub (pinned to e7288fc434)