chinabugotech/hutool · error · AIException

Failed to get remote file MIME type

Error message

Failed to get remote file MIME type

What it means

Thrown by GeminiServiceImpl.getRemoteFileMimeType when fetching metadata for a Gemini files/ resource fails. SAME library bug as error 11: it calls new AIException("Failed to get remote file MIME type", e.getMessage()), which resolves to the (String, Object... params) overload, so the underlying exception e is dropped and the real cause is lost from the stack trace. Triggered indirectly whenever a multimodal call references a files/ URI.

Source

Thrown at hutool-ai/src/main/java/cn/hutool/ai/model/gemini/GeminiServiceImpl.java:452

	private String getRemoteFileMimeType(String fileUri) {
		try {
			final HttpRequest httpRequest = HttpRequest.get(fileUri)
				.header(Header.ACCEPT, "application/json")
				.header("x-goog-api-key", config.getApiKey())
				.timeout(config.getTimeout());
			if (config.getHasProxy()) {
				httpRequest.setProxy(config.getProxy());
			}
			String responseBody = httpRequest.execute().body();
			final JSONObject json = JSONUtil.parseObj(responseBody);

			//提取服务端的mimeType
			String mimeType = json.getStr("mimeType");
			if (StrUtil.isNotBlank(mimeType)) {
				return mimeType;
			}
		} catch (Exception e) {
			throw new AIException("Failed to get remote file MIME type", e.getMessage());
		}
		return "application/octet-stream";
	}

	/**
	 * 发送Get请求
	 * @param endpoint 请求节点
	 * @return 请求响应
	 */
	@Override
	protected HttpResponse sendGet(String endpoint) {
		//链式构建请求
		try {
			//设置超时3分钟
			final HttpRequest httpRequest = HttpRequest.get(config.getApiUrl() + endpoint)
				.header(Header.ACCEPT, "application/json")
				.header("x-goog-api-key", config.getApiKey())
				.timeout(config.getTimeout());

View on GitHub (pinned to 8870454b2a)

Solutions

  1. Re-upload the file to obtain a fresh, valid files/ URI and use it promptly.
  2. Verify the apiKey is the same one used to upload and still valid.
  3. Since the cause is lost (library bug), call the metadata GET manually to see the real status/body.
  4. Confirm the files/ URI string is well-formed (starts with files/ or a full https URI).

Example fix

// before
String r = gemini.chat("describe", List.of("files/abc-123-old"));
// -> Failed to get remote file MIME type (cause lost)

// after -- re-upload and use a fresh uri
String up = gemini.uploadFile(myFile);
String uri = JSONUtil.parseObj(up).getByPath("file.uri", String.class);
String r = gemini.chat("describe", List.of(uri));
Defensive patterns

Strategy: validation

Validate before calling

// Validate the files/ URI + key freshness before the multimodal call
if (StrUtil.isBlank(media) || !media.contains("files/")) {
    throw new IllegalArgumentException("not a files/ URI: " + media);
}
if (StrUtil.isBlank(config.getApiKey())) throw new IllegalStateException("key missing");

Type guard

static boolean isGeminiFileUri(String m) {
    return m != null && m.contains("files/");
}

Try / catch

try {
    return gemini.chat(prompt, List.of(fileUri));
} catch (AIException e) {
    if ("Failed to get remote file MIME type".equals(e.getMessage())) {
        // cause is LOST (varargs-overload bug); GET the metadata manually to see status/body
    }
    throw e;
}

Prevention

When it happens

Trigger: A multimodal call passes a media string containing 'files/' whose URI is invalid, expired, belongs to another key/project, the apiKey (x-goog-api-key) is invalid/revoked, the metadata response is not valid JSON, or the network request fails.

Common situations: Uploaded file URI expired (Gemini files are temporary); key mismatch between upload and chat; a manually-constructed files/ URI that does not exist; reverse-proxy mangling the metadata endpoint; JSON parse failure when the endpoint returns an HTML error page.

Related errors


AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14). Data as JSON: /api/errors/204e2905bc9902d5. Report an issue: GitHub.