apache/druid · error · IllegalArgumentException

Invalid URI scheme [ ] must be [ ]

Error message

Invalid URI scheme [%s] must be [%s]

What it means

CloudObjectLocation.validateUriScheme checks that a URI's scheme case-insensitively matches an expected scheme (e.g. "s3" or "gs") and throws an IAE showing the full URI and the required scheme. It enforces that object locations in a cloud input source use the scheme of the configured storage type.

Solutions

  1. Change the URI scheme to the expected one (e.g. use s3://bucket/path for S3, gs://bucket/path for GCS).
  2. Convert s3a:// or s3n:// URIs to s3:// for Druid S3 input sources.
  3. Ensure the storage type of the inputSource matches the URI scheme of every listed object/prefix.

Example fix

// before
{"type":"google","uris":["s3://bucket/path/file.json"]}
// after
{"type":"google","uris":["gs://bucket/path/file.json"]}
Defensive patterns

Strategy: validation

Validate before calling

URI uri = URI.create(location);
String expected = "s3"; // storage type's scheme
if (uri.getScheme() == null || !uri.getScheme().equalsIgnoreCase(expected)) {
  throw new IllegalArgumentException("Invalid URI scheme [" + uri + "] must be [" + expected + "]");
}

Type guard

String requireScheme(URI uri, String expected) {
  return uri.getScheme() != null && uri.getScheme().equalsIgnoreCase(expected)
      ? uri.toString()
      : CloudObjectLocation.validateUriScheme(expected, uri).toString();
}

Try / catch

try {
  locations.add(new CloudObjectLocation(URI.create(objectUri)));
} catch (IllegalArgumentException e) {
  if (e.getMessage().startsWith("Invalid URI scheme")) {
    throw new SpecValidationError("Object URI scheme does not match the input source storage type: " + objectUri, e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Building a CloudObjectLocation or validating a URI whose scheme differs from the expected one, e.g. an "s3://" URI supplied where "gs" is required, an "http://" URI in an s3 inputSource, or "S3A://"-style URIs in S3 specs.

Common situations: Copy-pasting URIs between Google Cloud Storage and S3 ingestion specs; using EMR/hadoop-style s3a or s3n schemes in a Druid s3 inputSource; typos like "ss3://bucket"; forgetting the scheme entirely.

Understand the failure class

Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/9acc66f28909f5ac. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/data/input/impl/CloudObjectLocation.java:52

 *
 * The intention is that this is used as a common representation for storage objects as an alternative to dealing in
 * {@link URI} directly, but still provide a mechanism to round-trip with a URI.
 *
 * In common clouds, bucket names must be dns compliant:
 * https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html
 * https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata
 * https://cloud.google.com/storage/docs/naming
 *
 * The constructor ensures that bucket names are DNS compliant by checking that the URL encoded form of the bucket
 * matches the supplied value. Technically it should probably confirm that the bucket is also all lower-case, but
 * S3 has a legacy mode where buckets did not have to be compliant so we can't enforce that here unfortunately.
 */
public class CloudObjectLocation
{
  public static URI validateUriScheme(String scheme, URI uri)
  {
    if (!scheme.equalsIgnoreCase(uri.getScheme())) {
      throw new IAE("Invalid URI scheme [%s] must be [%s]", uri.toString(), scheme);
    }
    return uri;
  }

  private final String bucket;
  private final String path;

  @JsonCreator
  public CloudObjectLocation(@JsonProperty("bucket") String bucket, @JsonProperty("path") String path)
  {
    this.bucket = Preconditions.checkNotNull(StringUtils.maybeRemoveTrailingSlash(bucket),
                 "bucket name cannot be null. Please verify if bucket name adheres to naming rules");
    this.path = Preconditions.checkNotNull(StringUtils.maybeRemoveLeadingSlash(path));
    Preconditions.checkArgument(
        this.bucket.equals(StringUtils.urlEncode(this.bucket)),
        "bucket must follow DNS-compliant naming conventions"
    );
  }

View on GitHub (pinned to 9b90983fd2)