apereo/cas · warning

Failed to delete service definition file

Error message

Failed to delete service definition file [{}]

What it means

AbstractResourceBasedServiceRegistry.delete attempts to remove the on-disk file backing a registered service. If File.delete() fails (file missing write permission on the directory, locked by another process, or a race), CAS logs this warning and returns false, leaving the service definition file in place.

Solutions

  1. Check filesystem permissions on the services directory and file: the CAS process user needs write permission on both.
  2. Verify the volume is not mounted read-only (docker mount flags, k8s readOnlyRootFilesystem).
  3. Check for file locks or a process holding the file open, then retry the delete.
  4. Manually remove the stale file and reload the registry.

Example fix

// before: delete fails silently with warning
chown -R cas:cas /etc/cas/config/services
// after
chmod -R u+rw /etc/cas/config/services  # run as the CAS process user
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check before delete
File f = new File(filePath);
if (f.exists() && !f.canWrite()) throw new AccessDeniedException(filePath);
File dir = f.getParentFile();
if (!dir.canWrite()) throw new AccessDeniedException(dir.getPath());

Try / catch

// Treat false return as retryable
boolean deleted = serviceRegistry.delete(service);
if (!deleted) { /* check permissions/locks, retry with backoff or delete manually */ }

Prevention

When it happens

Trigger: Calling serviceRegistry.delete(service) (via ServicesManager or the management app) where filename.exists() but filename.delete() returns false; the publishEvent/removeRegisteredService path is skipped only for the warn log but result remains false.

Common situations: Services directory owned by a different user than the CAS process; read-only mounted volume; NFS/Windows file locks; file recreated by a watcher between existence check and delete.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: source

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

            LOGGER.debug("Saved service to [{}]", fileName.getCanonicalPath());
        } catch (final IOException e) {
            throw new IllegalArgumentException("IO error opening file stream.", e);
        }
        return findServiceById(service.getId());
    }

    @Override
    public boolean delete(final RegisteredService service) {
        return lock.tryLock(() -> FunctionUtils.doUnchecked(() -> {
            val filename = getRegisteredServiceFileName(service);
            val clientInfo = ClientInfoHolder.getClientInfo();
            publishEvent(new CasRegisteredServicePreDeleteEvent(this, service, clientInfo));
            val result = !filename.exists() || filename.delete();
            if (result) {
                removeRegisteredService(service);
                LOGGER.debug("Successfully deleted service definition file [{}]", filename.getCanonicalPath());
            } else {
                LOGGER.warn("Failed to delete service definition file [{}]", filename.getCanonicalPath());
            }
            publishEvent(new CasRegisteredServiceDeletedEvent(this, service, clientInfo));
            return result;
        }));
    }

    @Override
    public void deleteAll() {
        val files = FileUtils.listFiles(this.serviceRegistryDirectory.toFile(), getExtensions(), true);
        files.forEach(File::delete);
    }

    @Override
    public Collection<RegisteredService> load() {
        return lock.tryLock(() -> {
            LOGGER.trace("Loading files from [{}]", this.serviceRegistryDirectory);
            val serviceRegistryDirectoryFile = serviceRegistryDirectory.toFile();
            val files = serviceRegistryDirectoryFile.exists()

View on GitHub (pinned to e7288fc434)