SonarSource/sonarqube · error

Index [ ] is in read-only mode…

Error message

Index [{}] is in read-only mode (index.blocks.read_only_allow_delete=true)

What it means

When scanning per-index settings returned by the ES stats API, the task counts indices whose index.blocks.read_only_allow_delete block is true and logs one warning per blocked index. This block is typically set by Elasticsearch's flood-stage watermark to protect the disk, making the index read-only (deletes allowed).

Solutions

  1. Free disk space above the flood-stage watermark on the ES data node.
  2. Remove the block per index: PUT /<indexName>/_settings {"index.blocks.read_only_allow_delete": null}.
  3. Check cluster settings for any manually applied read_only_allow_delete blocks and clear them.
  4. Confirm via GET /_all/_settings that no blocks remain, then verify ES status turns Green in SonarQube monitoring.

Example fix

# before: index blocked
curl -XGET 'localhost:9001/sonarqube/_settings?pretty' # "read_only_allow_delete": "true"
# after: unblock once disk is freed
curl -XPUT 'localhost:9001/sonarqube/_settings' -H 'Content-Type: application/json' -d '{"index.blocks.read_only_allow_delete": null}'
Defensive patterns

Strategy: retry

Validate before calling

# verify no blocked indices before indexing
curl -s 'localhost:9001/_all/_settings' | jq '[to_entries[] | select(.value.settings.index.blocks.read_only_allow_delete=="true") | .key]'

Try / catch

const blocked = await getBlockedIndices();
if (blocked.length > 0) {
  await freeDiskSpaceThenRetry(async () => {
    for (const idx of blocked) await es.indices.putSettings({ index: idx, body: { 'index.blocks.read_only_allow_delete': null } });
  });
}

Prevention

When it happens

Trigger: updateAndGetReadOnlyIndicesCount (called from readOnlyIndicesCount path) iterating index states where settings().index().blocks().readOnlyAllowDelete() equals TRUE for a given index name.

Common situations: Disk crossed 95% on the data node causing ES to auto-block indices; indices left blocked after disk cleanup because the block is not removed automatically (pre-7.x behavior / manual setting); restored snapshots carrying block settings.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/08010a96f563fec9. Report an issue: GitHub.

Appendix: source

Thrown at server/sonar-webserver-monitoring/src/main/java/org/sonar/server/monitoring/ElasticSearchMetricTask.java:136

    } catch (Exception e) {
      LOG.error("Failed to query ES status", e);
    }
  }

  private long updateAndGetReadOnlyIndicesCount() {
    try {
      final GetIndicesSettingsResponse settingsResponse = esClient.getSettingsV2(req -> req.index("*"));
      final Map<String, IndexState> indices = settingsResponse.settings();

      long readOnlyCount = 0;
      for (Map.Entry<String, IndexState> entry : indices.entrySet()) {
        final String indexName = entry.getKey();
        final IndexState indexState = entry.getValue();

        if (indexState.settings() != null && indexState.settings().index() != null) {
          final var blocks = indexState.settings().index().blocks();
          if (blocks != null && Boolean.TRUE.equals(blocks.readOnlyAllowDelete())) {
            LOG.warn("Index [{}] is in read-only mode (index.blocks.read_only_allow_delete=true)", indexName);
            readOnlyCount++;
          }
        }
      }

      // Update metric for observability
      serverMonitoringMetrics.setElasticSearchReadOnlyIndicesCount(readOnlyCount);

      return readOnlyCount;
    } catch (Exception e) {
      LOG.error("Failed to check for readonly indices", e);
      // Return 0 on error to avoid false positives - let cluster health handle it
      return 0;
    }
  }

  private double updateAndGetMaxDiskUsagePercent(@Nullable NodeStatsResponse nodeStatsResponse, double thresholdPercent) {
    try {

View on GitHub (pinned to 184c821202)