{"record":{"id":"0b70deaea2704aed","repo":"elastic/elasticsearch","slug":"database-is-read-only-0b70de","errorCode":null,"errorMessage":"Database {} is read only","messagePattern":"Database (.+?) is read only","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"modules/ip-location/src/main/java/org/elasticsearch/ingest/geoip/direct/TransportDeleteDatabaseConfigurationAction.java","lineNumber":100,"sourceCode":"            \"delete-geoip-database-configuration-state-update\",\n            Priority.NORMAL,\n            DELETE_TASK_EXECUTOR\n        );\n        this.projectResolver = projectResolver;\n    }\n\n    @Override\n    protected void masterOperation(Task task, Request request, ClusterState state, ActionListener<AcknowledgedResponse> listener)\n        throws Exception {\n        final String id = request.getDatabaseId();\n        final ProjectId projectId = projectResolver.getProjectId();\n        final IngestGeoIpMetadata geoIpMeta = state.metadata()\n            .getProject(projectId)\n            .custom(IngestGeoIpMetadata.TYPE, IngestGeoIpMetadata.EMPTY);\n        if (geoIpMeta.getDatabases().containsKey(id) == false) {\n            throw new ResourceNotFoundException(\"Database configuration not found: {}\", id);\n        } else if (geoIpMeta.getDatabases().get(id).database().isReadOnly()) {\n            throw new IllegalArgumentException(\"Database \" + id + \" is read only\");\n        }\n        deleteDatabaseConfigurationTaskQueue.submitTask(\n            Strings.format(\"delete-geoip-database-configuration-[%s]\", id),\n            new DeleteDatabaseConfigurationTask(projectId, listener, id),\n            null\n        );\n    }\n\n    private record DeleteDatabaseConfigurationTask(ProjectId projectId, ActionListener<AcknowledgedResponse> listener, String databaseId)\n        implements\n            ClusterStateTaskListener {\n\n        ClusterState execute(ClusterState currentState) throws Exception {\n            final var project = currentState.metadata().getProject(projectId);\n            final IngestGeoIpMetadata geoIpMeta = project.custom(IngestGeoIpMetadata.TYPE, IngestGeoIpMetadata.EMPTY);\n\n            logger.debug(\"deleting database configuration [{}]\", databaseId);\n            Map<String, DatabaseConfigurationMetadata> databases = new HashMap<>(geoIpMeta.getDatabases());","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/modules/ip-location/src/main/java/org/elasticsearch/ingest/geoip/direct/TransportDeleteDatabaseConfigurationAction.java#L82-L118","documentation":"Thrown by TransportDeleteDatabaseConfigurationAction.masterOperation when the existing database configuration's provider is read-only (isReadOnly() == true, i.e. Local or Web). System-managed databases cannot be deleted by users because the downloader/service owns their lifecycle.","triggerScenarios":"DELETE _ingest/geoip/database/<id> where <id> resolves to a configuration whose provider is 'local' or 'web'. The existence check passes, then the read-only check fails.","commonSituations":"Trying to clean up a Local database created by the node's local GeoIP files; attempting to remove a Web/enterprise database; confusing system databases with user-defined Maxmind/Ipinfo ones.","solutions":["Only delete user-defined databases (provider 'maxmind' or 'ipinfo').","For Local databases, remove the underlying local files / disable the local source rather than issuing DELETE.","For Web databases, manage the subscription/entitlement on the Elastic side, not via DELETE."],"exampleFix":"// before: fails, 'my-local' is provider=local\nDELETE _ingest/geoip/database/my-local\n\n// after: delete a writable config instead\nDELETE _ingest/geoip/database/my-city-db","handlingStrategy":"validation","validationCode":"// Before DELETE, fetch the config and check its provider\nDatabaseConfiguration cfg = client.geoipDatabases().get(id);\nif (cfg != null && (\"local\".equals(cfg.provider()) || \"web\".equals(cfg.provider()))) {\n    throw new IllegalStateException(\"Cannot delete read-only provider \" + cfg.provider());\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only attempt DELETE on maxmind/ipinfo configurations.","Local databases are managed via filesystem; Web via subscription - not DELETE.","Filter read-only entries out of any bulk-cleanup script."],"tags":["elasticsearch","geoip","validation","provider","read-only","delete"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T08:17:17.861Z"}