apache/druid · error · IllegalStateException
A batch appenderator was already created for this peon's…
Error message
A batch appenderator was already created for this peon's task.
What it means
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.
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
Example fix
// before
manager.createBatchAppenderatorForTask(...);
manager.createRealtimeAppenderatorForTask(...); // throws
// after
if (isBatch) {
manager.createBatchAppenderatorForTask(...);
} else {
manager.createRealtimeAppenderatorForTask(...);
} Defensive patterns
Strategy: validation
Validate before calling
if (peonManager.batchAppenderatorExists()) throw new IllegalStateException("batch appenderator already present"); Type guard
boolean canCreateRealtime = !manager.hasBatchAppenderator();
Try / catch
try { manager.createRealtimeAppenderatorForTask(...); } catch (ISE e) { log.error("cannot mix batch and realtime appenderators", e); throw e; } Prevention
- Decide ingestion mode (batch vs realtime) once per task
- Never invoke both create methods in one peon
- Split mixed workloads into separate tasks
When it happens
Trigger: A task first created a batch appenderator via createBatchAppenderatorForTask and then attempts to create a realtime appenderator in the same peon process.
Common situations: Mixing batch and streaming ingestion logic in one custom task; misconfigured task type causing the wrong creation path to be invoked twice.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- A realtime appenderator was already created for this peon's…
- Can't add [ , ] to non-empty…
- Cannot have both awaitChannels and awaitFutures
- Conflicting key[ ] calculated via keyMapper for original…
- Expected single element
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/764a7370391e0060.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/main/java/org/apache/druid/segment/realtime/appenderator/PeonAppenderatorsManager.java:95
QueryRunnerFactoryConglomerate conglomerate,
DataSegmentAnnouncer segmentAnnouncer,
ServiceEmitter emitter,
QueryProcessingPool queryProcessingPool,
JoinableFactory joinableFactory,
Cache cache,
CacheConfig cacheConfig,
CachePopulatorStats cachePopulatorStats,
PolicyEnforcer policyEnforcer,
RowIngestionMeters rowIngestionMeters,
ParseExceptionHandler parseExceptionHandler,
CentralizedDatasourceSchemaConfig centralizedDatasourceSchemaConfig,
TaskIntervalUnlocker taskIntervalUnlocker
)
{
if (realtimeAppenderator != null) {
throw new ISE("A realtime appenderator was already created for this peon's task.");
} else if (batchAppenderator != null) {
throw new ISE("A batch appenderator was already created for this peon's task.");
} else {
realtimeAppenderator = Appenderators.createRealtime(
segmentLoaderConfig,
taskId,
schema,
config,
metrics,
dataSegmentPusher,
jsonMapper,
indexIO,
indexMerger,
conglomerate,
segmentAnnouncer,
emitter,
queryProcessingPool,
cache,
cacheConfig,
cachePopulatorStats,View on GitHub (pinned to 9b90983fd2)