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
- Add the required host (or wildcard pattern) to MediaFetcher's allowedHosts configuration.
- Fix the media URI so it has a valid, fully-qualified host.
- If apex domains should be allowed, add the apex explicitly since *.example.com does not match example.com.
- 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
- Keep the allowlist in sync with all media CDN domains you use
- Validate media URLs are absolute with a host before fetching
- Remember wildcards do not match apex domains
- Never fetch user-supplied URLs without allowlist enforcement
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
- Connection to blocked internal address rejected for host ''
- Media URL response exceeds maximum allowed size of bytes:
- Host '' resolves to a blocked internal address:
- URL host '' resolves to a blocked internal address:
- Failed to read media data from URL:
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/56587e69e04a889f.
Report an issue: GitHub.