We limit the number of concurrent blob downloads per node and per recovery, but if a user configures too many concurrent recoveries they may run out of per-node download permits which yields the following message:

> Unable to acquire permit to use snapshot files during recovery, this recovery will recover index files from the source node. Ensure snapshot files can be used during recovery by setting [indices.recovery.max_concurrent_snapshot_file_downloads] to be no greater than [25]

The `[25]` here is wrong, the 25 permits are shared across all inbound recoveries so we should divide the suggested value by `cluster.routing.allocation.node_concurrent_incoming_recoveries`. In extreme cases the user may have configured so many concurrent recoveries that the only reasonable limit is zero, and in that case we should suggest setting `indices.recovery.use_snapshots: false` instead.


server/src/main/java/org/elasticsearch/indices/recovery/RecoverySettings.java
);
            return null;
        }
        return Releasables.releaseOnce(() -> maxSnapshotFileDownloadsPerNodeSemaphore.release(maxConcurrentSnapshotFileDownloads));
    }
    private static void validateNodeBandwidthRecoverySettings(Settings settings) {
        final List<String> nonDefaults = NODE_BANDWIDTH_RECOVERY_SETTINGS.stream()
            .filter(setting -> setting.get(settings) != ByteSizeValue.MINUS_ONE)
            .map(Setting::getKey)
            .toList();
        if (nonDefaults.isEmpty() == false && nonDefaults.size() != NODE_BANDWIDTH_RECOVERY_SETTINGS.size()) {
            throw new IllegalArgumentException(
                "Settings "
                    + NODE_BANDWIDTH_RECOVERY_SETTINGS.stream().map(Setting::getKey).toList()
                    + " must all be defined or all be undefined; but only settings "
                    + nonDefaults
                    + " are configured."
            );
        }
    }
    /**
     * Whether the node bandwidth recovery settings are set.
     */
    private static boolean hasNodeBandwidthRecoverySettings(Settings settings) {
        return NODE_BANDWIDTH_RECOVERY_SETTINGS.stream()
            .filter(setting -> setting.get(settings) != ByteSizeValue.MINUS_ONE)
            .count() == NODE_BANDWIDTH_RECOVERY_SETTINGS.size();
    }
}