{"record":{"id":"5efc7b8bdfefcfd6","repo":"apache/druid","slug":"s3-requires-one-of-s-s-or-s","errorCode":null,"errorMessage":"S3 requires one of %s, %s or %s","messagePattern":"S3 requires one of (.+?), (.+?) or (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"extensions-core/s3-extensions/src/main/java/org/apache/druid/catalog/model/table/S3InputSourceDefn.java","lineNumber":222,"sourceCode":"    final boolean hasBucket = bucket != null;\n    final boolean hasPaths = !CollectionUtils.isNullOrEmpty(paths);\n    if (hasPaths && !hasBucket) {\n      throw new IAE(\n          \"S3 requires the %s parameter if %s is set\",\n          BUCKET_PARAMETER,\n          PATHS_PARAMETER\n      );\n    }\n    if ((hasUris && (hasPrefixes || hasBucket)) || (hasPrefixes && hasBucket)) {\n      throw new IAE(\n          \"S3 accepts only one of %s, %s or %s\",\n          PATHS_PARAMETER,\n          BUCKET_PARAMETER,\n          PREFIXES_PARAMETER\n      );\n    }\n    if (!hasUris && !hasPrefixes && !hasBucket) {\n      throw new IAE(\n          \"S3 requires one of %s, %s or %s\",\n          PATHS_PARAMETER,\n          BUCKET_PARAMETER,\n          PREFIXES_PARAMETER\n      );\n    }\n    if (hasUris) {\n      jsonMap.put(URIS_FIELD, CatalogUtils.stringListToUriList(uris));\n    }\n    if (hasPrefixes) {\n      jsonMap.put(PREFIXES_FIELD, prefixes);\n    }\n    if (hasBucket) {\n      if (!hasPaths) {\n        throw new IAE(\"When using the %s parameter, %s must also be provided\", BUCKET_PARAMETER, PATHS_PARAMETER);\n      }\n      List<CloudObjectLocation> objects = new ArrayList<>();\n      for (String obj : paths) {","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/s3-extensions/src/main/java/org/apache/druid/catalog/model/table/S3InputSourceDefn.java#L204-L240","documentation":"This IllegalArgumentException comes from the S3 external table definition in Druid's catalog when converting table-function arguments into an S3 input source map. An S3 input source needs at least one way to locate objects: URIs (paths), prefixes, or a bucket with paths. The library throws this when the caller provided none of them, because the resulting input source would have no data locations at all.","triggerScenarios":"Calling the S3 external table function (e.g. EXTERN with TABLE functions) without supplying any of the 'paths', 'bucket', or 'prefixes' parameters, or supplying only properties that do not map to one of these three parameters so all three boolean checks (hasUris, hasPrefixes, hasBucket) are false.","commonSituations":"Writing SQL EXTERN queries against an S3 catalog table and forgetting to pass the table function parameters; passing only credentials/endpoint properties; renaming or misspelling parameter names so the values never bind; building arguments programmatically with an empty map.","solutions":["Add at least one location parameter to the table function call: 'paths' (list of S3 URIs), 'prefixes' (list of prefixes), or 'bucket' together with 'paths'.","If using a bucket, also pass the 'paths' parameter listing object keys in that bucket.","Verify parameter names match the catalog definition exactly (uri/uris/paths vs prefixes vs bucket).","If the intent was to use a fully catalog-defined table, ensure the catalog input source itself defines URIs/prefixes and remove the expectation that the function supplies them."],"exampleFix":"// before\nSELECT * FROM TABLE(EXTERN('s3-ext' => ARRAY[]))\n// after\nSELECT * FROM TABLE(EXTERN(\n  's3-ext' => ARRAY['s3://my-bucket/data/file.json']\n))","handlingStrategy":"validation","validationCode":"boolean hasUris = args != null && !args.getOrDefault(\"uris\", List.of()).isEmpty();\nboolean hasPaths = args != null && !args.getOrDefault(\"paths\", List.of()).isEmpty();\nboolean hasPrefixes = args != null && !args.getOrDefault(\"prefixes\", List.of()).isEmpty();\nboolean hasBucket = args != null && args.get(\"bucket\") != null;\nif (!hasUris && !hasPrefixes && !(hasBucket && hasPaths)) {\n  throw new IllegalArgumentException(\"S3 external table requires one of: uris/paths, prefixes, or bucket + paths\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass at least one of paths, prefixes, or bucket+paths in the table function call","Use prefixes when you want whole-prefix listing rather than bucket alone","Validate the argument map before invoking EXTERN","Keep parameter names copied from the catalog definition verbatim"],"tags":["validation","input-source","s3","missing-argument"],"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"}