{"record":{"id":"4bd0b8aaac9043bf","repo":"apache/pulsar","slug":"late-data-topic-can-be-defined-only-when-specifyin","errorCode":null,"errorMessage":"Late data topic can be defined only when specifying a timestamp extractor class","messagePattern":"Late data topic can be defined only when specifying a timestamp extractor class","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-functions/instance/src/main/java/org/apache/pulsar/functions/windowing/WindowFunctionExecutor.java","lineNumber":115,"sourceCode":"                WindowConfig.class);\n\n        return windowConfig;\n    }\n\n    private WindowManager<Record<T>> getWindowManager(WindowConfig windowConfig, Context context) {\n\n        WindowLifecycleListener<Event<Record<T>>> lifecycleListener = newWindowLifecycleListener(context);\n        WindowManager<Record<T>> manager = new WindowManager<>(lifecycleListener, new ConcurrentLinkedQueue<>());\n\n        if (this.windowConfig.getTimestampExtractorClassName() != null) {\n            this.timestampExtractor = getTimeStampExtractor(windowConfig);\n\n            waterMarkEventGenerator = new WaterMarkEventGenerator<>(manager, this.windowConfig\n                    .getWatermarkEmitIntervalMs(),\n                    this.windowConfig.getMaxLagMs(), new HashSet<>(context.getInputTopics()), context);\n        } else {\n            if (this.windowConfig.getLateDataTopic() != null) {\n                throw new IllegalArgumentException(\n                        \"Late data topic can be defined only when specifying a timestamp extractor class\");\n            }\n        }\n\n        EvictionPolicy<Record<T>, ?> evictionPolicy = getEvictionPolicy(windowConfig);\n        TriggerPolicy<Record<T>, ?> triggerPolicy = getTriggerPolicy(windowConfig, manager,\n                evictionPolicy, context);\n        manager.setEvictionPolicy(evictionPolicy);\n        manager.setTriggerPolicy(triggerPolicy);\n\n        return manager;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    private TimestampExtractor<T> getTimeStampExtractor(WindowConfig windowConfig) {\n\n        Class<?> theCls;\n        try {","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-functions/instance/src/main/java/org/apache/pulsar/functions/windowing/WindowFunctionExecutor.java#L97-L133","documentation":"A late-data topic lets the executor route records arriving after the watermark, but routing them correctly requires a custom timestamp extractor to know each record's event time. getWindowManager throws this IllegalArgumentException if lateDataTopic is set in WindowConfig while timestampExtractorClassName is null. The configuration is internally inconsistent, so initialization fails.","triggerScenarios":"WindowConfig supplied with lateDataTopic non-null but timestampExtractorClassName not set; typically a copy-pasted window config where the extractor class was removed or never added.","commonSituations":"Users enabling late-data handling for out-of-order events but relying on the default ingestion-time timestamps; config template stripping the extractor class name; switching from ingestion-time to event-time semantics without cleaning lateDataTopic.","solutions":["Set timestampExtractorClassName in WindowConfig to a class implementing org.apache.pulsar.functions.api.windowing.TimestampExtractor","Or remove lateDataTopic from WindowConfig if you don't need late-data routing (late events are then dropped by the eviction policy)","Rebuild and redeploy the function with the corrected window config","Document the extractor class and ensure its jar is bundled with the function"],"exampleFix":"// before\nuserConfig: { \"windowConfig\": { \"windowLengthDurationMs\": 60000, \"lateDataTopic\": \"late-topic\" } }\n// after\nuserConfig: { \"windowConfig\": { \"windowLengthDurationMs\": 60000, \"lateDataTopic\": \"late-topic\",\n  \"timestampExtractorClassName\": \"com.example.MyTimestampExtractor\" } }","handlingStrategy":"validation","validationCode":"WindowConfig wc = ...;\nif (wc.getLateDataTopic() != null && wc.getTimestampExtractorClassName() == null) {\n    throw new IllegalArgumentException(\"lateDataTopic requires timestampExtractorClassName\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    executor.initialize();\n} catch (IllegalArgumentException e) {\n    throw new IllegalStateException(\"inconsistent window config: \" + e.getMessage(), e);\n}","preventionTips":["Treat lateDataTopic and timestampExtractorClassName as a pair in config templates","Drop lateDataTopic if you rely on ingestion-time timestamps","Validate window config JSON in CI before deploying"],"tags":["pulsar-functions","windowing","late-data","invalid-config"],"backgroundTag":"invalid-window-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"}