{"record":{"id":"e504dc515af31f83","repo":"apereo/cas","slug":"the-service-definition-file-could-not-be-saved-at","errorCode":null,"errorMessage":"The service definition file could not be saved at ","messagePattern":"The service definition file could not be saved at ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"core/cas-server-core-services-registry/src/main/java/org/apereo/cas/services/resource/AbstractResourceBasedServiceRegistry.java","lineNumber":194,"sourceCode":"    }\n\n    @Override\n    public @Nullable RegisteredService save(final RegisteredService service) {\n        service.assignIdIfNecessary();\n        val fileName = getRegisteredServiceFileName(service);\n        try (val out = Files.newOutputStream(fileName.toPath())) {\n            invokeServiceRegistryListenerPreSave(service);\n            val result = registeredServiceSerializers.stream().anyMatch(serializer -> {\n                try {\n                    serializer.to(out, service);\n                    return true;\n                } catch (final Exception e) {\n                    LOGGER.debug(e.getMessage(), e);\n                    return false;\n                }\n            });\n            if (!result) {\n                throw new IOException(\"The service definition file could not be saved at \" + fileName.getCanonicalPath());\n            }\n            if (this.services.containsKey(service.getId())) {\n                LOGGER.debug(\"Found existing service definition by id [{}]. Saving...\", service.getId());\n            }\n            services.put(service.getId(), service);\n            LOGGER.debug(\"Saved service to [{}]\", fileName.getCanonicalPath());\n        } catch (final IOException e) {\n            throw new IllegalArgumentException(\"IO error opening file stream.\", e);\n        }\n        return findServiceById(service.getId());\n    }\n\n    @Override\n    public boolean delete(final RegisteredService service) {\n        return lock.tryLock(() -> FunctionUtils.doUnchecked(() -> {\n            val filename = getRegisteredServiceFileName(service);\n            val clientInfo = ClientInfoHolder.getClientInfo();\n            publishEvent(new CasRegisteredServicePreDeleteEvent(this, service, clientInfo));","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-services-registry/src/main/java/org/apereo/cas/services/resource/AbstractResourceBasedServiceRegistry.java#L176-L212","documentation":"The service registry computed a target file for the RegisteredService, but the underlying store (RepositoryFilter/RepositoryResult check inside the save lambda) returned false, so AbstractResourceBasedServiceRegistry.save aborts with an IOException stating the service definition file could not be written at that path. The registry's in-memory map is left unchanged.","triggerScenarios":"save() is called (add/save of a RegisteredService via the management console or registry API) and the storeIfNotPresent/overwrite lambda fails — typically because the service directory doesn't exist and can't be created, the process lacks write permission, a filter (e.g. an inline repository filter or duplicate serviceId check) rejects the file, or the filesystem is read-only/full.","commonSituations":"CAS service registry directory on a read-only mount or container volume; wrong cas.service-registry.* path permissions after running as a different user; disk full; a custom RepositoryFilter rejecting the save; running multiple CAS nodes with mismatched shared-filesystem setups.","solutions":["Read the preceding debug-level log line (the caught exception's message) to see why the store returned false — it names the actual failure.","Verify the service registry directory exists and the CAS process user can create/write files in it (chown/chmod, or fix cas.service-registry.json.directory).","Check disk space and whether the mount is read-only (e.g. Kubernetes volume mounted ro).","Review any configured custom RepositoryFilter beans for rules rejecting the file (duplicate serviceId, name patterns).","If replication-related, ensure the shared filesystem is consistently mounted on all CAS nodes before retrying the save."],"exampleFix":"// before: registry directory not writable by cas user\ncas.service-registry.json.directory=/etc/cas/services  (owned by root, mode 755)\n// after: grant the CAS process write access\nchown cas:cas /etc/cas/services && chmod 750 /etc/cas/services","handlingStrategy":"try-catch","validationCode":"File dir = new File(casServiceRegistryDirectory);\nif (!dir.canWrite() || (dir.exists() && !dir.isDirectory()) && !dir.mkdirs()) {\n    // fix permissions/path before saving services\n}","typeGuard":null,"tryCatchPattern":"try {\n    serviceRegistry.save(service);\n} catch (IOException e) {\n    LOGGER.error(\"Service save failed: {}\", e.getMessage());\n    // check registry dir permissions/space before retry\n}","preventionTips":["Provision the registry directory with correct ownership for the CAS runtime user.","Monitor disk space and mount flags (read-only) on registry volumes.","Catch and surface the debug log emitted just before the IOException — it holds the root cause.","Test service saves after deploying to new containers or volume layouts."],"tags":["service-registry","io","filesystem","persistence"],"backgroundTag":"file-write-failed","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}