{"record":{"id":"764a7370391e0060","repo":"apache/druid","slug":"a-batch-appenderator-was-already-created-for-this","errorCode":null,"errorMessage":"A batch appenderator was already created for this peon's task.","messagePattern":"A batch appenderator was already created for this peon's task\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"server/src/main/java/org/apache/druid/segment/realtime/appenderator/PeonAppenderatorsManager.java","lineNumber":95,"sourceCode":"      QueryRunnerFactoryConglomerate conglomerate,\n      DataSegmentAnnouncer segmentAnnouncer,\n      ServiceEmitter emitter,\n      QueryProcessingPool queryProcessingPool,\n      JoinableFactory joinableFactory,\n      Cache cache,\n      CacheConfig cacheConfig,\n      CachePopulatorStats cachePopulatorStats,\n      PolicyEnforcer policyEnforcer,\n      RowIngestionMeters rowIngestionMeters,\n      ParseExceptionHandler parseExceptionHandler,\n      CentralizedDatasourceSchemaConfig centralizedDatasourceSchemaConfig,\n      TaskIntervalUnlocker taskIntervalUnlocker\n  )\n  {\n    if (realtimeAppenderator != null) {\n      throw new ISE(\"A realtime appenderator was already created for this peon's task.\");\n    } else if (batchAppenderator != null) {\n      throw new ISE(\"A batch appenderator was already created for this peon's task.\");\n    } else {\n      realtimeAppenderator = Appenderators.createRealtime(\n          segmentLoaderConfig,\n          taskId,\n          schema,\n          config,\n          metrics,\n          dataSegmentPusher,\n          jsonMapper,\n          indexIO,\n          indexMerger,\n          conglomerate,\n          segmentAnnouncer,\n          emitter,\n          queryProcessingPool,\n          cache,\n          cacheConfig,\n          cachePopulatorStats,","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/server/src/main/java/org/apache/druid/segment/realtime/appenderator/PeonAppenderatorsManager.java#L77-L113","documentation":"createRealtimeAppenderatorForTask throws this ISE when a batch appenderator has already been created for this peon. A peon can host either a realtime or a batch appenderator, but never both, since the manager holds single fields for each.","triggerScenarios":"A task first created a batch appenderator via createBatchAppenderatorForTask and then attempts to create a realtime appenderator in the same peon process.","commonSituations":"Mixing batch and streaming ingestion logic in one custom task; misconfigured task type causing the wrong creation path to be invoked twice.","solutions":["Do not mix batch and realtime appenderator creation in one peon; pick one per task","Create a separate task for the batch portion of the workload","Refactor custom task code so the ingestion type is decided once before any appenderator is created"],"exampleFix":"// before\nmanager.createBatchAppenderatorForTask(...);\nmanager.createRealtimeAppenderatorForTask(...); // throws\n// after\nif (isBatch) {\n  manager.createBatchAppenderatorForTask(...);\n} else {\n  manager.createRealtimeAppenderatorForTask(...);\n}","handlingStrategy":"validation","validationCode":"if (peonManager.batchAppenderatorExists()) throw new IllegalStateException(\"batch appenderator already present\");","typeGuard":"boolean canCreateRealtime = !manager.hasBatchAppenderator();","tryCatchPattern":"try { manager.createRealtimeAppenderatorForTask(...); } catch (ISE e) { log.error(\"cannot mix batch and realtime appenderators\", e); throw e; }","preventionTips":["Decide ingestion mode (batch vs realtime) once per task","Never invoke both create methods in one peon","Split mixed workloads into separate tasks"],"tags":["java","appenderator","illegal-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}