{"record":{"id":"95d187c7f29fc229","repo":"apache/pulsar","slug":"message-retries-not-yet-supported-in-go-function","errorCode":null,"errorMessage":"Message retries not yet supported in Go function","messagePattern":"Message retries not yet supported in Go function","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java","lineNumber":775,"sourceCode":"            throw new IllegalArgumentException(\"There is currently no support windowing in python\");\n        }\n\n        if (functionConfig.getMaxMessageRetries() != null && functionConfig.getMaxMessageRetries() >= 0) {\n            throw new IllegalArgumentException(\"Message retries not yet supported in python\");\n        }\n    }\n\n    private static void doGolangChecks(FunctionConfig functionConfig) {\n        if (functionConfig.getProcessingGuarantees() == FunctionConfig.ProcessingGuarantees.EFFECTIVELY_ONCE) {\n            throw new RuntimeException(\"Effectively-once processing guarantees not yet supported in Go function\");\n        }\n\n        if (functionConfig.getWindowConfig() != null) {\n            throw new IllegalArgumentException(\"Windowing is not supported in Go function yet\");\n        }\n\n        if (functionConfig.getMaxMessageRetries() != null && functionConfig.getMaxMessageRetries() >= 0) {\n            throw new IllegalArgumentException(\"Message retries not yet supported in Go function\");\n        }\n    }\n\n    private static void verifyNoTopicClash(Collection<String> inputTopics, String outputTopic)\n            throws IllegalArgumentException {\n        if (inputTopics.contains(outputTopic)) {\n            throw new IllegalArgumentException(\n                    String.format(\n                            \"Output topic %s is also being used as an input topic (topics must be one or the other)\",\n                            outputTopic));\n        }\n    }\n\n    public static void doCommonChecks(FunctionConfig functionConfig) {\n        if (isEmpty(functionConfig.getTenant())) {\n            throw new IllegalArgumentException(\"Function tenant cannot be null\");\n        }\n        if (isEmpty(functionConfig.getNamespace())) {","sourceCodeStart":757,"sourceCodeEnd":793,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/utils/src/main/java/org/apache/pulsar/functions/utils/FunctionConfigUtils.java#L757-L793","documentation":"Like the Python runtime, the Go function runtime does not implement message retries, so doGolangChecks rejects any Go function config with maxMessageRetries set to a non-negative value. This prevents deploying a Go function that silently ignores retry semantics.","triggerScenarios":"Creating or updating a Go function with setMaxMessageRetries(N) where N != null and N >= 0, or passing --max-message-retries with a --go function create/update command; validated via validateNonJavaFunction -> doGolangChecks.","commonSituations":"Uniform function-deployment pipelines that set retry counts for all runtimes; retry configs ported from Java functions to Go; operators enabling retries to survive transient processing failures in Go functions.","solutions":["Remove maxMessageRetries (set to null) or omit --max-message-retries for the Go function","Implement retry logic within the Go function (catch errors, retry with backoff, or republish to a retry/DLQ topic)","Use the Java runtime if built-in message retries are required"],"exampleFix":"// before\nFunctionConfig config = new FunctionConfig();\nconfig.setRuntime(FunctionConfig.Runtime.GO);\nconfig.setMaxMessageRetries(5);\n\n// after\nFunctionConfig config = new FunctionConfig();\nconfig.setRuntime(FunctionConfig.Runtime.GO);\nconfig.setMaxMessageRetries(null); // retries unsupported in Go runtime","handlingStrategy":"validation","validationCode":"if (config.getRuntime() == FunctionConfig.Runtime.GO\n        && config.getMaxMessageRetries() != null\n        && config.getMaxMessageRetries() >= 0) {\n    throw new IllegalArgumentException(\"maxMessageRetries is unsupported for Go functions; remove it\");\n}\nFunctionConfigUtils.validateFunctionConfig(config, null);","typeGuard":"boolean goRetriesAllowed(FunctionConfig c) {\n    return c.getRuntime() != FunctionConfig.Runtime.GO\n        || c.getMaxMessageRetries() == null\n        || c.getMaxMessageRetries() < 0;\n}","tryCatchPattern":"try {\n    FunctionConfigUtils.validateFunctionConfig(config, null);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"Message retries not yet supported in Go\")) {\n        config.setMaxMessageRetries(null); // drop unsupported option for Go runtime\n    } else {\n        throw e;\n    }\n}","preventionTips":["Keep retry settings out of Go and Python function configs","Use runtime-specific deployment manifests rather than one shared FunctionConfig","Implement retries in Go function code (backoff + DLQ topic) instead of maxMessageRetries","Dry-run function create/update commands before production rollout"],"tags":["pulsar-functions","go-runtime","unsupported-feature","retries"],"backgroundTag":"unsupported-feature-for-runtime","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}