spring-projects/spring-ai · error · SecurityException

Unsupported URL protocol:

Error message

Unsupported URL protocol: 

What it means

Thrown when Media data is a java.net.URL whose protocol is not http or https. mapMediaToContentBlock enforces an allowlist of HTTP/HTTPS to prevent SSRF-style access via file:, ftp:, jar:, etc. The protocol is included in the message.

Source

Thrown at models/spring-ai-bedrock-converse/src/main/java/org/springframework/ai/bedrock/converse/BedrockProxyChatModel.java:563

						catch (SecurityException | RestClientException e) {
							throw new RuntimeException("Failed to read media data from URL: " + text, e);
						}
					}
					else {
						throw new SecurityException("URL is not valid under strict validation rules: " + text);
					}
				}
				else {
					// Assume it's base64-encoded image data
					sourceBuilder.bytes(SdkBytes.fromByteArray(Base64.getDecoder().decode(text)));
				}
			}
			else if (media.getData() instanceof URL url) {

				try {
					String protocol = url.getProtocol();
					if (!"http".equalsIgnoreCase(protocol) && !"https".equalsIgnoreCase(protocol)) {
						throw new SecurityException("Unsupported URL protocol: " + protocol);
					}
					byte[] bytes = this.mediaFetcher.fetch(url.toURI());
					sourceBuilder.bytes(SdkBytes.fromByteArrayUnsafe(bytes)).build();
				}
				catch (SecurityException | RestClientException | URISyntaxException e) {
					throw new IllegalArgumentException("Failed to read media data from URL: " + url, e);
				}
			}
			else {
				throw new IllegalArgumentException("Invalid Image content type: " + media.getData().getClass());
			}

			return ContentBlock.fromImage(ImageBlock.builder()
				.source(sourceBuilder.build())
				.format(BedrockMediaFormat.getImageFormat(mimeType))
				.build());
		}
		else if (BedrockMediaFormat.isSupportedDocumentFormat(mimeType)) { // Document

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Switch to an http/https URL for the media resource.
  2. Load the local resource yourself and pass byte[]/base64 data instead of a URL.
  3. Upload the asset to an accessible HTTP endpoint (e.g. S3 with public/signed URL over https).
  4. Check url.getProtocol() before constructing Media to fail fast with a clear message.

Example fix

// before
new Media(MimeTypeUtils.IMAGE_PNG, new URL("file:///img/cat.png"));
// after
byte[] bytes = Files.readAllBytes(Path.of("/img/cat.png"));
new Media(MimeTypeUtils.IMAGE_PNG, bytes);
Defensive patterns

Strategy: type-guard

Validate before calling

String proto = url.getProtocol();
if (!"http".equalsIgnoreCase(proto) && !"https".equalsIgnoreCase(proto)) {
    throw new IllegalArgumentException("Only http/https media URLs supported, got: " + proto);
}

Type guard

boolean isHttpUrl(URL url) {
    return url != null && ("http".equalsIgnoreCase(url.getProtocol()) || "https".equalsIgnoreCase(url.getProtocol()));
}

Try / catch

try {
    model.call(prompt);
} catch (IllegalArgumentException e) {
    if (e.getCause() instanceof SecurityException se && se.getMessage().contains("Unsupported URL protocol")) {
        // load bytes locally and resend with byte[] data
    } else throw e;
}

Prevention

When it happens

Trigger: Constructing Media with new URL("file:///path/img.png"), ftp://, or any non-HTTP(S) URL and sending it to the Bedrock model. Also triggered when the URL itself is fine but the subsequent fetch/URI conversion throws SecurityException, RestClientException, or URISyntaxException, which are rewrapped (see error 303).

Common situations: Developers building media from local files or classpath resources using file:// URLs; test fixtures using ftp servers; migrating code from other AI libraries that accepted file URLs.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/b321b30ff53f23a1. Report an issue: GitHub.