{"record":{"id":"6a76c30a004d75b6","repo":"apache/pulsar","slug":"function-timeout-must-be-a-positive-number","errorCode":null,"errorMessage":"Function timeout must be a positive number","messagePattern":"Function timeout must be a positive number","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java","lineNumber":860,"sourceCode":"\n        WindowConfig windowConfig = functionConfig.getWindowConfig();\n        if (windowConfig != null) {\n            // set auto ack to false since windowing framework is responsible\n            // for acking and not the function framework\n            @SuppressWarnings(\"deprecation\")\n            Boolean windowAutoAck = functionConfig.getAutoAck();\n            if (windowAutoAck != null && windowAutoAck) {\n                throw new IllegalArgumentException(\"Cannot enable auto ack when using windowing functionality\");\n            }\n            WindowConfigUtils.validate(windowConfig);\n        }\n\n        if (functionConfig.getResources() != null) {\n            ResourceConfigUtils.validate(functionConfig.getResources());\n        }\n\n        if (functionConfig.getTimeoutMs() != null && functionConfig.getTimeoutMs() <= 0) {\n            throw new IllegalArgumentException(\"Function timeout must be a positive number\");\n        }\n\n        if (functionConfig.getTimeoutMs() != null\n                && functionConfig.getProcessingGuarantees() != null\n                && functionConfig.getProcessingGuarantees() != FunctionConfig.ProcessingGuarantees.ATLEAST_ONCE) {\n            throw new IllegalArgumentException(\"Message timeout can only be specified with processing guarantee is \"\n                    + FunctionConfig.ProcessingGuarantees.ATLEAST_ONCE.name());\n        }\n\n        if (functionConfig.getMaxMessageRetries() != null && functionConfig.getMaxMessageRetries() >= 0\n                && functionConfig.getProcessingGuarantees() == FunctionConfig.ProcessingGuarantees.EFFECTIVELY_ONCE) {\n            throw new IllegalArgumentException(\"MaxMessageRetries and Effectively once don't gel well\");\n        }\n        if ((functionConfig.getMaxMessageRetries() == null || functionConfig.getMaxMessageRetries() < 0)\n                && !org.apache.commons.lang3.StringUtils.isEmpty(functionConfig.getDeadLetterTopic())) {\n            throw new IllegalArgumentException(\"Dead Letter Topic specified, however max retries is set to infinity\");\n        }\n        if (functionConfig.getRetainKeyOrdering() != null","sourceCodeStart":842,"sourceCodeEnd":878,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java#L842-L878","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set timeoutMs to a positive millisecond value, e.g. 30000 for 30s","To disable timeout, leave the field null/unset rather than 0","Guard computed values: only setTimeoutMs when value > 0"],"exampleFix":"// before\nconfig.setTimeoutMs(timeoutMs); // may be 0\n// after\nif (timeoutMs != null && timeoutMs > 0) {\n    config.setTimeoutMs(timeoutMs);\n}","handlingStrategy":"validation","validationCode":"Long t = config.getTimeoutMs();\nif (t != null && t <= 0) {\n    throw new IllegalArgumentException(\"timeoutMs must be positive, got \" + t);\n}","typeGuard":"boolean positiveTimeout(FunctionConfig c) {\n    return c.getTimeoutMs() == null || c.getTimeoutMs() > 0;\n}","tryCatchPattern":"try {\n    admin.functions().updateFunction(functionConfig, configLocation);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"timeout must be a positive number\")) {\n        // clear or correct timeoutMs and resubmit\n    }\n    throw e;\n}","preventionTips":["Leave timeoutMs unset rather than 0 when no timeout is desired","Parse durations into milliseconds carefully when converting from config files","Validate timeoutMs > 0 in config generation code","Pair timeout checks with processingGuarantees validation (see error 1417)"],"tags":["pulsar-functions","validation","timeout","configuration"],"backgroundTag":"invalid-function-config","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}