{"record":{"id":"9acc66f28909f5ac","repo":"apache/druid","slug":"invalid-uri-scheme-s-must-be-s","errorCode":null,"errorMessage":"Invalid URI scheme [%s] must be [%s]","messagePattern":"Invalid URI scheme \\[(.+?)\\] must be \\[(.+?)\\]","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/data/input/impl/CloudObjectLocation.java","lineNumber":52,"sourceCode":" *\n * The intention is that this is used as a common representation for storage objects as an alternative to dealing in\n * {@link URI} directly, but still provide a mechanism to round-trip with a URI.\n *\n * In common clouds, bucket names must be dns compliant:\n * https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html\n * https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata\n * https://cloud.google.com/storage/docs/naming\n *\n * The constructor ensures that bucket names are DNS compliant by checking that the URL encoded form of the bucket\n * matches the supplied value. Technically it should probably confirm that the bucket is also all lower-case, but\n * S3 has a legacy mode where buckets did not have to be compliant so we can't enforce that here unfortunately.\n */\npublic class CloudObjectLocation\n{\n  public static URI validateUriScheme(String scheme, URI uri)\n  {\n    if (!scheme.equalsIgnoreCase(uri.getScheme())) {\n      throw new IAE(\"Invalid URI scheme [%s] must be [%s]\", uri.toString(), scheme);\n    }\n    return uri;\n  }\n\n  private final String bucket;\n  private final String path;\n\n  @JsonCreator\n  public CloudObjectLocation(@JsonProperty(\"bucket\") String bucket, @JsonProperty(\"path\") String path)\n  {\n    this.bucket = Preconditions.checkNotNull(StringUtils.maybeRemoveTrailingSlash(bucket),\n                 \"bucket name cannot be null. Please verify if bucket name adheres to naming rules\");\n    this.path = Preconditions.checkNotNull(StringUtils.maybeRemoveLeadingSlash(path));\n    Preconditions.checkArgument(\n        this.bucket.equals(StringUtils.urlEncode(this.bucket)),\n        \"bucket must follow DNS-compliant naming conventions\"\n    );\n  }","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/data/input/impl/CloudObjectLocation.java#L34-L70","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Change the URI scheme to the expected one (e.g. use s3://bucket/path for S3, gs://bucket/path for GCS).","Convert s3a:// or s3n:// URIs to s3:// for Druid S3 input sources.","Ensure the storage type of the inputSource matches the URI scheme of every listed object/prefix."],"exampleFix":"// before\n{\"type\":\"google\",\"uris\":[\"s3://bucket/path/file.json\"]}\n// after\n{\"type\":\"google\",\"uris\":[\"gs://bucket/path/file.json\"]}","handlingStrategy":"validation","validationCode":"URI uri = URI.create(location);\nString expected = \"s3\"; // storage type's scheme\nif (uri.getScheme() == null || !uri.getScheme().equalsIgnoreCase(expected)) {\n  throw new IllegalArgumentException(\"Invalid URI scheme [\" + uri + \"] must be [\" + expected + \"]\");\n}","typeGuard":"String requireScheme(URI uri, String expected) {\n  return uri.getScheme() != null && uri.getScheme().equalsIgnoreCase(expected)\n      ? uri.toString()\n      : CloudObjectLocation.validateUriScheme(expected, uri).toString();\n}","tryCatchPattern":"try {\n  locations.add(new CloudObjectLocation(URI.create(objectUri)));\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().startsWith(\"Invalid URI scheme\")) {\n    throw new SpecValidationError(\"Object URI scheme does not match the input source storage type: \" + objectUri, e);\n  }\n  throw e;\n}","preventionTips":["Match URI schemes to the inputSource type: s3 for S3, gs for GCS, azure for Azure.","Rewrite s3a:// and s3n:// URIs to s3:// when moving configs from Hadoop to Druid.","Include the scheme explicitly in every object/prefix URI; never rely on defaults.","Lint spec files for scheme/storage-type mismatches in CI before deployment."],"tags":["java","illegal-argument","uri","input-source"],"backgroundTag":"invalid-url","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"}