{"record":{"id":"36a2ab4b47686da3","repo":"apache/druid","slug":"granularityspec-s-intervals-cannot-be-empty-for-re","errorCode":null,"errorMessage":"GranularitySpec's intervals cannot be empty for replace.","messagePattern":"GranularitySpec's intervals cannot be empty for replace\\.","errorType":"validation","errorClass":"IAE","httpStatus":null,"severity":"error","filePath":"indexing-service/src/main/java/org/apache/druid/indexing/common/task/IndexTask.java","lineNumber":1075,"sourceCode":"    private final IndexTuningConfig tuningConfig;\n\n    @JsonCreator\n    public IndexIngestionSpec(\n        @JsonProperty(\"dataSchema\") DataSchema dataSchema,\n        @JsonProperty(\"ioConfig\") IndexIOConfig ioConfig,\n        @JsonProperty(\"tuningConfig\") IndexTuningConfig tuningConfig\n    )\n    {\n      super(dataSchema, ioConfig, tuningConfig);\n\n      InvalidInput.notNull(ioConfig.getInputSource(), \"inputSource\");\n\n      IngestionMode ingestionMode = AbstractTask.computeBatchIngestionMode(ioConfig);\n\n      if (ingestionMode == IngestionMode.REPLACE && dataSchema.getGranularitySpec()\n                                                              .inputIntervals()\n                                                              .isEmpty()) {\n        throw new IAE(\"GranularitySpec's intervals cannot be empty for replace.\");\n      }\n\n      if (ioConfig.getInputSource().needsFormat()) {\n        InvalidInput.notNull(ioConfig.getInputFormat(), \"inputFormat\");\n      }\n\n      this.dataSchema = dataSchema;\n      this.ioConfig = ioConfig;\n      this.tuningConfig = tuningConfig == null ? new IndexTuningConfig() : tuningConfig;\n    }\n\n    @Override\n    @JsonProperty(\"dataSchema\")\n    public DataSchema getDataSchema()\n    {\n      return dataSchema;\n    }\n","sourceCodeStart":1057,"sourceCodeEnd":1093,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/indexing-service/src/main/java/org/apache/druid/indexing/common/task/IndexTask.java#L1057-L1093","documentation":"When constructing an IndexTask whose ioConfig implies IngestionMode.REPLACE, the constructor requires that GranularitySpec.inputIntervals is non-empty: a replace must know exactly which intervals it is overwriting so it can unpublish/overwrite those segments. An empty interval set is rejected with this IAE at task-creation time.","triggerScenarios":"Submitting an index (batch) task with ioConfig type \"replace\" (or tuning/context making ingestionMode REPLACE) while granularitySpec has no explicit intervals — e.g., omitting \"intervals\" in granularitySpec, which is allowed for append but not replace.","commonSituations":"Converting an append task spec to replace by only changing ioConfig type; generating specs from templates that omit intervals; SQL/JSON tooling that drops null intervals; users unfamiliar with the replace-mode requirement.","solutions":["Add explicit \"intervals\" to granularitySpec covering the data being replaced","If the intent is append, set ioConfig type to \"index\" (append) instead of \"replace\"","Compute intervals from input data before submitting the task and inject them into the spec","Validate the spec (replace requires non-empty intervals) before submission"],"exampleFix":"// before\n\"ioConfig\": { \"type\": \"replace\" },\n\"granularitySpec\": { \"type\": \"uniform\", \"segmentGranularity\": \"day\" } // no intervals\n// after\n\"ioConfig\": { \"type\": \"replace\" },\n\"granularitySpec\": { \"type\": \"uniform\", \"segmentGranularity\": \"day\", \"intervals\": [\"2022-01-01/2022-01-31\"] }","handlingStrategy":"validation","validationCode":"if (\"replace\".equals(ioConfig.getType())\n    && granularitySpec.inputIntervals().isEmpty()) {\n  throw new IllegalArgumentException(\"Replace mode requires non-empty intervals in granularitySpec\");\n}","typeGuard":"boolean replaceReady(IndexIOConfig io, GranularitySpec gs) {\n  return !IngestionMode.REPLACE.equals(AbstractTask.computeBatchIngestionMode(io))\n      || !gs.inputIntervals().isEmpty();\n}","tryCatchPattern":"try {\n  IndexTask task = new IndexTask(id, group, resource, cloneTaskId, schema, mapper, auth, context);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"intervals cannot be empty for replace\")) {\n    schema = schema.withGranularitySpec(gs.withIntervals(computedIntervals));\n    task = rebuild(schema);\n  } else throw e;\n}","preventionTips":["Always set intervals when ioConfig type is replace","Add spec linting that checks replace + empty intervals","Compute intervals from input data during spec generation","Default append mode when intervals are absent"],"tags":["druid","ingestion","replace","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}