apache/druid · error · IllegalArgumentException

Only %s protocols are allowed

Error message

Only %s protocols are allowed

What it means

HttpInputSource.throwIfInvalidProtocols validates every URI against druid.input.http.allowedProtocols (default http and https). Any URI whose scheme is not on the allowlist is rejected with IAE before any fetch is attempted, protecting against SSRF to file/ftp/other schemes.

Source

Thrown at processing/src/main/java/org/apache/druid/data/input/impl/HttpInputSource.java:96

      @JacksonInject HttpInputSourceConfig config
  )
  {
    Preconditions.checkArgument(uris != null && !uris.isEmpty(), "Empty URIs");
    throwIfInvalidProtocols(config, uris);
    this.uris = uris;
    this.httpAuthenticationUsername = httpAuthenticationUsername;
    this.httpAuthenticationPasswordProvider = httpAuthenticationPasswordProvider;
    this.systemFields = systemFields == null ? SystemFields.none() : systemFields;
    this.requestHeaders = requestHeaders == null ? Collections.emptyMap() : requestHeaders;
    throwIfForbiddenHeaders(config, this.requestHeaders);
    this.config = config;
  }

  public static void throwIfInvalidProtocols(HttpInputSourceConfig config, List<URI> uris)
  {
    for (URI uri : uris) {
      if (!config.getAllowedProtocols().contains(StringUtils.toLowerCase(uri.getScheme()))) {
        throw new IAE("Only %s protocols are allowed", config.getAllowedProtocols());
      }
    }
  }

  public static void throwIfForbiddenHeaders(HttpInputSourceConfig config, Map<String, String> requestHeaders)
  {
    for (Map.Entry<String, String> entry : requestHeaders.entrySet()) {
      if (!config.getAllowedHeaders().contains(StringUtils.toLowerCase(entry.getKey()))) {
        throw InvalidInput.exception("Got forbidden header [%s], allowed headers are only [%s]. You can control the allowed headers by updating druid.ingestion.http.allowedHeaders",
                                     entry.getKey(), config.getAllowedHeaders()
        );
      }
    }
  }

  @JsonIgnore
  @Nonnull
  @Override

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Change the URI to use an allowed scheme (http/https by default)
  2. Add the needed scheme to the 'druid.input.http.allowedProtocols' runtime property, e.g. -Ddruid.input.http.allowedProtocols=["http","https"]
  3. Use the input source type matching the scheme (S3InputSource, LocalInputSource) instead of HttpInputSource

Example fix

// before
"inputSource": {"type":"http","uris":["s3://bucket/data.json"]}
// after
"inputSource": {"type":"http","uris":["https://example.com/data.json"]}
// or allow the scheme: -Ddruid.input.http.allowedProtocols=["https","s3"]
Defensive patterns

Strategy: validation

Validate before calling

// Before creating the input source
List<String> allowed = getAllowedProtocols(); // from druid.input.http.allowedProtocols
for (URI u : uris) {
    if (!allowed.contains(u.getScheme().toLowerCase(Locale.ROOT))) {
        throw new IllegalArgumentException("Scheme not allowed: " + u);
    }
}

Try / catch

try { new HttpInputSource(uris, headers, systemFields, config); } catch (IAE e) { /* rewrite URIs to https or pick a matching input source type */ }

Prevention

When it happens

Trigger: Creating an HttpInputSource with a URI whose scheme (e.g. file, s3, ftp, or an uppercase variant blocked by config) is not present in HttpInputSourceConfig.getAllowedProtocols().

Common situations: Pasting an s3:// or file:// URI into an http input source; enterprise deployments that restrict allowedProtocols to https only while specs use http://; machine-generated specs carrying non-HTTP URIs.

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/caa6ffe980cf3a78. Report an issue: GitHub.