jeecgboot/JeecgBoot · error · RuntimeException
发送消息失败,读取文件异常:
Error message
发送消息失败,读取文件异常:
What it means
This error is thrown by AIChatHandler.buildImageContents() when an IOException occurs while reading a local image file for base64 encoding. The method constructs a file path by concatenating uploadpath with the imageUrl, reads the file bytes, encodes to base64, and probes the MIME type. If Files.readAllBytes or Files.probeContentType throws IOException, it is wrapped in a RuntimeException.
Source
Thrown at jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/llm/handler/AIChatHandler.java:499
if (matcher.matches()) {
// 来源于网络
imageContents.add(ImageContent.from(imageUrl));
} else {
// 本地文件
String filePath = uploadpath + File.separator + imageUrl;
// 读取文件并转换为 base64 编码字符串
try {
SsrfFileTypeFilter.checkPathTraversal(filePath);
Path path = Paths.get(filePath);
byte[] fileContent = Files.readAllBytes(path);
String base64Data = Base64.getEncoder().encodeToString(fileContent);
// 获取文件的 MIME 类型
String mimeType = Files.probeContentType(path);
// 构建 ImageContent 对象
imageContents.add(ImageContent.from(base64Data, mimeType));
} catch (IOException e) {
log.error("读取文件失败: {}", imageUrl, e);
throw new RuntimeException("发送消息失败,读取文件异常:" + e.getMessage(), e);
}
}
}
return imageContents;
}
//================================================= begin【QQYUN-12145】【AI】AI 绘画创作 ========================================
/**
* 文本生成图片
* @param modelId
* @param messages
* @param params
* @return
*/
@Override
public List<Map<String, Object>> imageGenerate(String modelId, String messages, AIChatParams params) {
AssertUtils.assertNotEmpty("至少发送一条消息", messages);
//AssertUtils.assertNotEmpty("请选择图片大模型", modelId);View on GitHub (pinned to 96fb33f5ec)
Solutions
- Verify the image file exists at uploadpath + File.separator + imageUrl on the server filesystem.
- Check that the application process has read permissions on the upload directory and files.
- Confirm the jeecg.path.upload configuration matches the actual upload directory.
- If files are stored on a network share, verify mount stability and connectivity.
- Add existence and readability checks before Files.readAllBytes for better error messages.
Example fix
// before
byte[] fileContent = Files.readAllBytes(path);
// after — check existence and readability first
if (!Files.exists(path) || !Files.isReadable(path)) {
throw new RuntimeException("Image file not found or not readable: " + imageUrl);
}
byte[] fileContent = Files.readAllBytes(path); Defensive patterns
Strategy: validation
Validate before calling
// Before reading, verify the file exists and is readable
String filePath = uploadpath + File.separator + imageUrl;
Path path = Paths.get(filePath);
if (!Files.exists(path)) {
log.warn("Image file does not exist: {}", filePath);
return Collections.emptyList(); // or throw a descriptive error
}
if (!Files.isReadable(path)) {
log.warn("Image file is not readable: {}", filePath);
return Collections.emptyList();
}
SsrfFileTypeFilter.checkPathTraversal(filePath); Type guard
public static boolean isImageReadable(String uploadpath, String imageUrl) {
try {
Path path = Paths.get(uploadpath + File.separator + imageUrl);
return Files.exists(path) && Files.isRegularFile(path) && Files.isReadable(path);
} catch (InvalidPathException e) {
return false;
}
} Try / catch
try {
byte[] fileContent = Files.readAllBytes(path);
String base64Data = Base64.getEncoder().encodeToString(fileContent);
imageContents.add(ImageContent.from(base64Data, mimeType));
} catch (IOException e) {
log.error("Failed to read image file: {}", imageUrl, e);
// Skip this image rather than failing the entire request
// Or throw with a more specific message
throw new RuntimeException("Image read failed for '" + imageUrl + "': " + e.getMessage(), e);
} Prevention
- Verify file existence before attempting to read
- Ensure uploadpath configuration matches the actual file storage location
- Check file permissions for the application process
- Handle missing images gracefully — skip or return partial results rather than failing the entire chat
When it happens
Trigger: A chat request with local image attachments where Files.readAllBytes(path) fails due to: file not found at the expected path, permission denied, file is a directory, disk I/O error, or the path is invalid after traversal checks pass. SsrfFileTypeFilter.checkPathTraversal is called first but only checks for traversal patterns, not existence.
Common situations: The image file was deleted or moved between upload and chat processing. The uploadpath configuration points to a directory that doesn't exist or has wrong permissions. The imageUrl contains a relative path that doesn't resolve correctly. Disk space or I/O issues. The file exists but is locked by another process.
Related errors
- 图片读取失败:
- 非法文件路径,禁止访问上传目录之外的文件:
- Illegal access to path outside of base directory.
- 非法业务路径,禁止访问上传目录之外的路径: ${bizPath}
- result.getMessage()
AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14).
Data as JSON: /api/errors/c9b80aecc9a4a5a4.
Report an issue: GitHub.