apache/pulsar · error · IllegalArgumentException

Function timeout must be a positive number

Error message

Function timeout must be a positive number

What it means

timeoutMs defines how long a message may take to process before being redelivered, which only makes sense with ATLEAST_ONCE guarantees and must be a positive duration. doCommonChecks throws this IllegalArgumentException when timeoutMs is set to zero or a negative value.

Source

Thrown at pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java:860

        WindowConfig windowConfig = functionConfig.getWindowConfig();
        if (windowConfig != null) {
            // set auto ack to false since windowing framework is responsible
            // for acking and not the function framework
            @SuppressWarnings("deprecation")
            Boolean windowAutoAck = functionConfig.getAutoAck();
            if (windowAutoAck != null && windowAutoAck) {
                throw new IllegalArgumentException("Cannot enable auto ack when using windowing functionality");
            }
            WindowConfigUtils.validate(windowConfig);
        }

        if (functionConfig.getResources() != null) {
            ResourceConfigUtils.validate(functionConfig.getResources());
        }

        if (functionConfig.getTimeoutMs() != null && functionConfig.getTimeoutMs() <= 0) {
            throw new IllegalArgumentException("Function timeout must be a positive number");
        }

        if (functionConfig.getTimeoutMs() != null
                && functionConfig.getProcessingGuarantees() != null
                && functionConfig.getProcessingGuarantees() != FunctionConfig.ProcessingGuarantees.ATLEAST_ONCE) {
            throw new IllegalArgumentException("Message timeout can only be specified with processing guarantee is "
                    + FunctionConfig.ProcessingGuarantees.ATLEAST_ONCE.name());
        }

        if (functionConfig.getMaxMessageRetries() != null && functionConfig.getMaxMessageRetries() >= 0
                && functionConfig.getProcessingGuarantees() == FunctionConfig.ProcessingGuarantees.EFFECTIVELY_ONCE) {
            throw new IllegalArgumentException("MaxMessageRetries and Effectively once don't gel well");
        }
        if ((functionConfig.getMaxMessageRetries() == null || functionConfig.getMaxMessageRetries() < 0)
                && !org.apache.commons.lang3.StringUtils.isEmpty(functionConfig.getDeadLetterTopic())) {
            throw new IllegalArgumentException("Dead Letter Topic specified, however max retries is set to infinity");
        }
        if (functionConfig.getRetainKeyOrdering() != null

View on GitHub (pinned to 820761864e)

Solutions

  1. Set timeoutMs to a positive millisecond value, e.g. 30000 for 30s
  2. To disable timeout, leave the field null/unset rather than 0
  3. Guard computed values: only setTimeoutMs when value > 0

Example fix

// before
config.setTimeoutMs(timeoutMs); // may be 0
// after
if (timeoutMs != null && timeoutMs > 0) {
    config.setTimeoutMs(timeoutMs);
}
Defensive patterns

Strategy: validation

Validate before calling

Long t = config.getTimeoutMs();
if (t != null && t <= 0) {
    throw new IllegalArgumentException("timeoutMs must be positive, got " + t);
}

Type guard

boolean positiveTimeout(FunctionConfig c) {
    return c.getTimeoutMs() == null || c.getTimeoutMs() > 0;
}

Try / catch

try {
    admin.functions().updateFunction(functionConfig, configLocation);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("timeout must be a positive number")) {
        // clear or correct timeoutMs and resubmit
    }
    throw e;
}

Prevention

When it happens

Trigger: createFunction/updateFunction with functionConfig.getTimeoutMs() != null && timeoutMs <= 0: e.g. --timeout-ms 0 on the CLI, a variable defaulting to 0 assigned into the config, or a misparsed duration from YAML.

Common situations: Programmatic config where a Long timeout field defaults to 0, template placeholders like ${TIMEOUT_MS} resolved to empty/0, confusion between 'no timeout' (leave null) and 0.

Understand the failure class

Related errors


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