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
- 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.
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
- 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.
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
- Database {} is read only
- field [{}] is null, cannot extract geoip information.
- array in field [{}] should only contain strings
- field [{}] should contain only string or array of strings
- Exactly one provider object must be specified, but [{}] were
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/0b70deaea2704aed.
Report an issue: GitHub.