spring-projects/spring-ai · error · java.lang.SecurityException

Host '' is not in the allowed hosts list. Configure MediaFet

Error message

Host '' is not in the allowed hosts list. Configure MediaFetcher with the appropriate allowed hosts.

What it means

MediaFetcher.fetch() enforces an SSRF-style allowlist: when allowedHosts is non-empty and the URI's host does not match any allowed host (including wildcard rules), a SecurityException is thrown before any HTTP request is made. This prevents fetching media from unapproved or internal hosts.

Source

Thrown at models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/api/MediaFetcher.java:147

	/**
	 * Fetches the content at {@code uri} and returns it as a byte array.
	 *
	 * <p>
	 * The caller is responsible for validating the URI (protocol, host) before invoking
	 * this method. This method enforces size limits and socket-level SSRF protection.
	 * @param uri the URI to fetch
	 * @return the response body as a byte array
	 * @throws SecurityException if the response exceeds
	 * {@link #DEFAULT_MAX_FETCH_SIZE_BYTES} or the host resolves to a blocked internal
	 * address
	 * @throws org.springframework.web.client.RestClientException on HTTP or I/O errors
	 */
	public byte[] fetch(URI uri) {
		if (!this.allowedHosts.isEmpty()) {
			String host = uri.getHost();
			if (!isHostAllowed(host)) {
				throw new SecurityException("Host '" + host
						+ "' is not in the allowed hosts list. Configure MediaFetcher with the appropriate allowed hosts.");
			}
		}
		return this.restClient.get().uri(uri).exchange((request, response) -> {
			long contentLength = response.getHeaders().getContentLength();
			if (contentLength > DEFAULT_MAX_FETCH_SIZE_BYTES) {
				throw new SecurityException("Media URL response exceeds maximum allowed size of "
						+ DEFAULT_MAX_FETCH_SIZE_BYTES + " bytes: " + uri);
			}
			try (InputStream body = response.getBody()) {
				return readWithSizeLimit(body, DEFAULT_MAX_FETCH_SIZE_BYTES);
			}
		}, true);
	}

	/**
	 * Returns {@code true} if {@code host} is permitted by the allowlist. An entry that
	 * starts with {@code *.} is treated as a suffix wildcard matching any subdomain (e.g.

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Add the required host (or wildcard pattern) to MediaFetcher's allowedHosts configuration.
  2. Fix the media URI so it has a valid, fully-qualified host.
  3. If apex domains should be allowed, add the apex explicitly since *.example.com does not match example.com.
  4. If no restriction is desired, configure the fetcher with an empty allowedHosts (allow-all) — only for trusted inputs.

Example fix

// before
new MediaFetcher(restClient, List.of("media.example.com"));
// fetch(new URI("https://cdn.other.org/img.png"))
// after
new MediaFetcher(restClient, List.of("media.example.com", "cdn.other.org"));
Defensive patterns

Strategy: validation

Validate before calling

URI uri = URI.create(mediaUrl);
if (uri.getHost() == null || !isHostAllowedByConfig(uri.getHost())) {
    throw new IllegalArgumentException("Media URL host not allowed: " + uri.getHost());
}

Type guard

static boolean hasAllowedHost(URI uri, List<String> allowed) {
    String h = uri.getHost();
    return h != null && (allowed.isEmpty() || allowed.stream().anyMatch(p -> hostMatches(p, h)));
}

Try / catch

try {
    byte[] data = mediaFetcher.fetch(uri);
} catch (SecurityException e) {
    // skip media or fix allowlist configuration
}

Prevention

When it happens

Trigger: Calling fetch(uri) where uri's host is not in the MediaFetcher's configured allowedHosts list (and no wildcard rule matches), e.g. host '' when the URI has no host, or an external host while only internal hosts are allowed.

Common situations: Misconfigured allowed-hosts list missing a new media CDN domain; URIs without a host (relative or malformed) producing host ''; wildcard patterns like *.example.com not covering apex example.com; internal metadata addresses deliberately blocked.

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 spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/56587e69e04a889f. Report an issue: GitHub.