elastic/elasticsearch · error · IllegalArgumentException

Database {} is read only

Error message

Database {} is read only

What it means

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.

Source

Thrown at modules/ip-location/src/main/java/org/elasticsearch/ingest/geoip/direct/TransportDeleteDatabaseConfigurationAction.java:100

            "delete-geoip-database-configuration-state-update",
            Priority.NORMAL,
            DELETE_TASK_EXECUTOR
        );
        this.projectResolver = projectResolver;
    }

    @Override
    protected void masterOperation(Task task, Request request, ClusterState state, ActionListener<AcknowledgedResponse> listener)
        throws Exception {
        final String id = request.getDatabaseId();
        final ProjectId projectId = projectResolver.getProjectId();
        final IngestGeoIpMetadata geoIpMeta = state.metadata()
            .getProject(projectId)
            .custom(IngestGeoIpMetadata.TYPE, IngestGeoIpMetadata.EMPTY);
        if (geoIpMeta.getDatabases().containsKey(id) == false) {
            throw new ResourceNotFoundException("Database configuration not found: {}", id);
        } else if (geoIpMeta.getDatabases().get(id).database().isReadOnly()) {
            throw new IllegalArgumentException("Database " + id + " is read only");
        }
        deleteDatabaseConfigurationTaskQueue.submitTask(
            Strings.format("delete-geoip-database-configuration-[%s]", id),
            new DeleteDatabaseConfigurationTask(projectId, listener, id),
            null
        );
    }

    private record DeleteDatabaseConfigurationTask(ProjectId projectId, ActionListener<AcknowledgedResponse> listener, String databaseId)
        implements
            ClusterStateTaskListener {

        ClusterState execute(ClusterState currentState) throws Exception {
            final var project = currentState.metadata().getProject(projectId);
            final IngestGeoIpMetadata geoIpMeta = project.custom(IngestGeoIpMetadata.TYPE, IngestGeoIpMetadata.EMPTY);

            logger.debug("deleting database configuration [{}]", databaseId);
            Map<String, DatabaseConfigurationMetadata> databases = new HashMap<>(geoIpMeta.getDatabases());

View on GitHub (pinned to db6a809a66)

Solutions

  1. Only delete user-defined databases (provider 'maxmind' or 'ipinfo').
  2. For Local databases, remove the underlying local files / disable the local source rather than issuing DELETE.
  3. For Web databases, manage the subscription/entitlement on the Elastic side, not via DELETE.

Example fix

// before: fails, 'my-local' is provider=local
DELETE _ingest/geoip/database/my-local

// after: delete a writable config instead
DELETE _ingest/geoip/database/my-city-db
Defensive patterns

Strategy: validation

Validate before calling

// Before DELETE, fetch the config and check its provider
DatabaseConfiguration cfg = client.geoipDatabases().get(id);
if (cfg != null && ("local".equals(cfg.provider()) || "web".equals(cfg.provider()))) {
    throw new IllegalStateException("Cannot delete read-only provider " + cfg.provider());
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/0b70deaea2704aed. Report an issue: GitHub.