apache/pulsar · error · IllegalArgumentException

ioThreads must be >= 1

Error message

ioThreads must be >= 1

What it means

ConnectionPolicy requires ioThreads >= 1 because ioThreads sets the number of Netty event-loop threads handling the client's network I/O; zero or negative thread counts would make the client unable to process socket events. The constructor throws IllegalArgumentException for smaller values.

Source

Thrown at pulsar-client-api-v5/src/main/java/org/apache/pulsar/client/api/v5/config/ConnectionPolicy.java:67

    private ConnectionPolicy(Duration connectionTimeout,
                             int connectionsPerBroker,
                             boolean enableTcpNoDelay,
                             Duration keepAliveInterval,
                             Duration connectionMaxIdleTime,
                             int ioThreads,
                             int callbackThreads,
                             String proxyServiceUrl,
                             ProxyProtocol proxyProtocol,
                             BackoffPolicy connectionBackoff) {
        Objects.requireNonNull(connectionTimeout, "connectionTimeout must not be null");
        Objects.requireNonNull(keepAliveInterval, "keepAliveInterval must not be null");
        Objects.requireNonNull(connectionMaxIdleTime, "connectionMaxIdleTime must not be null");
        Objects.requireNonNull(connectionBackoff, "connectionBackoff must not be null");
        if (connectionsPerBroker < 1) {
            throw new IllegalArgumentException("connectionsPerBroker must be >= 1");
        }
        if (ioThreads < 1) {
            throw new IllegalArgumentException("ioThreads must be >= 1");
        }
        if (callbackThreads < 1) {
            throw new IllegalArgumentException("callbackThreads must be >= 1");
        }
        this.connectionTimeout = connectionTimeout;
        this.connectionsPerBroker = connectionsPerBroker;
        this.enableTcpNoDelay = enableTcpNoDelay;
        this.keepAliveInterval = keepAliveInterval;
        this.connectionMaxIdleTime = connectionMaxIdleTime;
        this.ioThreads = ioThreads;
        this.callbackThreads = callbackThreads;
        this.proxyServiceUrl = proxyServiceUrl;
        this.proxyProtocol = proxyProtocol;
        this.connectionBackoff = connectionBackoff;
    }

    /**
     * @return the maximum duration to wait for a TCP connection to a broker

View on GitHub (pinned to 820761864e)

Solutions

  1. Set ioThreads to at least 1 (a common choice is the number of CPU cores).
  2. Clamp computed values: Math.max(1, Runtime.getRuntime().availableProcessors() / 2).
  3. Omit the ioThreads call to use the library default.

Example fix

// before
int threads = Runtime.getRuntime().availableProcessors() / 8; // 0 on a 4-core box
ConnectionPolicy cp = ConnectionPolicy.builder()
    .ioThreads(threads) // IllegalArgumentException
    .build();

// after
int threads = Math.max(1, Runtime.getRuntime().availableProcessors() / 8);
ConnectionPolicy cp = ConnectionPolicy.builder()
    .ioThreads(threads)
    .build();
Defensive patterns

Strategy: validation

Validate before calling

int threads = Math.max(1, cfg.ioThreads() > 0 ? cfg.ioThreads() : Runtime.getRuntime().availableProcessors());
ConnectionPolicy cp = ConnectionPolicy.builder().ioThreads(threads).build();

Type guard

static boolean isValidIoThreads(int v) { return v >= 1; }

Try / catch

try {
    cp = ConnectionPolicy.builder().ioThreads(threads).build();
} catch (IllegalArgumentException e) {
    log.warn("Invalid ioThreads, using default", e);
    cp = ConnectionPolicy.builder().build();
}

Prevention

When it happens

Trigger: Calling ConnectionPolicy.builder().ioThreads(0) or a negative value; sizing ioThreads from available processors with a formula that returns 0 (e.g. Runtime.getRuntime().availableProcessors() / N when N exceeds core count).

Common situations: Container CPU limits making availableProcessors() small, so a division-based sizing formula yields 0; using 0 as a 'default' sentinel; config parsing an empty value into 0.

Understand the failure class

Background: "Invalid configuration value" and "Unsupported/Unknown setting value" errors: why libraries reject your config strings, numbers, and types — this error's family across 30 libraries.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/c2e5254008a30b09. Report an issue: GitHub.