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

  1. Verify the image file exists at uploadpath + File.separator + imageUrl on the server filesystem.
  2. Check that the application process has read permissions on the upload directory and files.
  3. Confirm the jeecg.path.upload configuration matches the actual upload directory.
  4. If files are stored on a network share, verify mount stability and connectivity.
  5. 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

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


AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14). Data as JSON: /api/errors/c9b80aecc9a4a5a4. Report an issue: GitHub.