{"record":{"id":"574e7794e7c61422","repo":"apache/druid","slug":"exactly-one-of-uris-prefixes-or-objects-must-be-s","errorCode":null,"errorMessage":"Exactly one of uris, prefixes or objects must be specified","messagePattern":"Exactly one of uris, prefixes or objects must be specified","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/data/input/impl/CloudObjectInputSource.java","lineNumber":318,"sourceCode":"\n  private void illegalArgsChecker() throws IllegalArgumentException\n  {\n    if (!CollectionUtils.isNullOrEmpty(objects)) {\n      throwIfIllegalArgs(!CollectionUtils.isNullOrEmpty(uris) || !CollectionUtils.isNullOrEmpty(prefixes));\n    } else if (!CollectionUtils.isNullOrEmpty(uris)) {\n      throwIfIllegalArgs(!CollectionUtils.isNullOrEmpty(prefixes));\n      uris.forEach(uri -> CloudObjectLocation.validateUriScheme(scheme, uri));\n    } else if (!CollectionUtils.isNullOrEmpty(prefixes)) {\n      prefixes.forEach(uri -> CloudObjectLocation.validateUriScheme(scheme, uri));\n    } else {\n      throwIfIllegalArgs(true);\n    }\n  }\n\n  private void throwIfIllegalArgs(boolean clause) throws IllegalArgumentException\n  {\n    if (clause) {\n      throw new IllegalArgumentException(\"Exactly one of uris, prefixes or objects must be specified\");\n    }\n  }\n\n  /**\n   * Stream of {@link InputSplit} for situations where this object is based on {@link #getPrefixes()}.\n   *\n   * If {@link CloudObjectSplitWidget#getDescriptorIteratorForPrefixes} returns objects with known sizes (as most\n   * implementations do), this method filters out empty objects.\n   */\n  private static Stream<InputSplit<List<CloudObjectLocation>>> getSplitsForPrefixes(\n      final InputFormat inputFormat,\n      final CloudObjectSplitWidget splitWidget,\n      final SplitHintSpec splitHintSpec,\n      final List<URI> prefixes,\n      @Nullable final String objectGlob\n  )\n  {\n    Iterator<CloudObjectSplitWidget.LocationWithSize> iterator =","sourceCodeStart":300,"sourceCodeEnd":336,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/data/input/impl/CloudObjectInputSource.java#L300-L336","documentation":"CloudObjectInputSource requires exactly one of the three ways to identify cloud objects: uris, prefixes, or objects. The constructor validates this via throwIfIllegalArgs and throws IllegalArgumentException when zero or more than one of them is set. This prevents ambiguous input source definitions that cannot be split into tasks.","triggerScenarios":"Submitting an ingestion spec with a CloudObjectInputSource (S3/GS/Azure) that sets none of uris/prefixes/objects, or sets both uris and prefixes, or both prefixes and objects; programmatically building an InputSource leaving all three null.","commonSituations":"Hand-edited JSON ingestion specs with leftover keys after switching from URI listing to prefix listing; template-generated specs where an optional field was not removed; copying an S3 spec and adding an objects field without removing prefixes.","solutions":["Keep exactly one of uris, prefixes, or objects in the inputSource spec and delete the others.","If none is set, add the appropriate property for your storage (e.g. \"prefixes\": [\"s3://bucket/path/\"]).","Migrate older specs: replace the deprecated uris form with objects (each with bucket/path) or prefixes."],"exampleFix":"// before\n{\"type\":\"s3\",\"prefixes\":[\"s3://b/p/\"],\"objects\":[{\"bucket\":\"b\",\"path\":\"p/f.json\"}]}\n// after\n{\"type\":\"s3\",\"prefixes\":[\"s3://b/p/\"]}","handlingStrategy":"validation","validationCode":"int count = 0;\nif (spec.has(\"uris\")) count++;\nif (spec.has(\"prefixes\")) count++;\nif (spec.has(\"objects\")) count++;\nif (count != 1) throw new IllegalArgumentException(\"Exactly one of uris, prefixes or objects must be specified\");","typeGuard":"function assertCloudSource(src) {\n  const set = [src.uris, src.prefixes, src.objects].filter(x => x != null);\n  if (set.length !== 1) throw new Error('Exactly one of uris, prefixes or objects must be specified');\n  return src;\n}","tryCatchPattern":"try {\n  InputSource src = jsonMapper.readValue(specJson, InputSource.class);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().startsWith(\"Exactly one of uris\")) {\n    throw new SpecValidationError(\"inputSource must define exactly one of uris/prefixes/objects\", e);\n  }\n  throw e;\n}","preventionTips":["Validate ingestion specs (druid validate or ingestion spec schema) before submission.","When editing specs by hand, remove old listing properties instead of adding new ones alongside.","Standardize on the objects form for new S3/GS specs and avoid mixing legacy uris.","Unit-test input source construction for all three configuration modes."],"tags":["java","illegal-argument","ingestion-spec","input-source"],"backgroundTag":"mutually-exclusive-options","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"}