jeecgboot/JeecgBoot · error · JeecgBootException

图片读取失败:

Error message

图片读取失败: 

What it means

This error is thrown by AIChatHandler.getFirstImageBase64() in the catch block when any exception occurs during image file processing that is not caught by the inner path-traversal check. The catch wraps all exceptions (including the path-traversal JeecgBootException from line 628) into a generic '图片读取失败' message with the imageUrl appended. This means the specific path-traversal error from error 98 gets re-wrapped here, losing the security-specific message.

Source

Thrown at jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/llm/handler/AIChatHandler.java:636

                        }
                    } else {
                        //update-begin---author:liusq ---date:2026-03-30  for:【issues/9431】修复getFirstImageBase64路径遍历漏洞(CWE-22)-----------
                        // 本地文件
                        String filePath = uploadpath + File.separator + imageUrl;
                        SsrfFileTypeFilter.checkPathTraversal(filePath);
                        // 路径遍历校验:规范化后确保文件在uploadpath目录内
                        File uploadDir = new File(uploadpath).getCanonicalFile();
                        File targetFile = new File(filePath).getCanonicalFile();
                        if (!targetFile.toPath().startsWith(uploadDir.toPath())) {
                            throw new JeecgBootException("非法文件路径,禁止访问上传目录之外的文件: " + imageUrl);
                        }
                        fileContent = Files.readAllBytes(targetFile.toPath());
                        //update-end---author:liusq ---date:2026-03-30  for:【issues/9431】修复getFirstImageBase64路径遍历漏洞(CWE-22)-----------
                    }
                    originalImageBase64List.add(Base64.getEncoder().encodeToString(fileContent));
                } catch (Exception e) {
                    log.error("图片读取失败: {}", imageUrl, e);
                    throw new JeecgBootException("图片读取失败: " + imageUrl);
                }
            }
        }
        return originalImageBase64List;
    }
    //================================================= end 【QQYUN-12145】【AI】AI 绘画创作 ========================================

    /**
     * 将 LLM 调用异常统一翻译为友好的 JeecgBootException。
     * <p>
     * 处理优先级:
     * <ol>
     *   <li>请求超时(timeout)→ 排队提示</li>
     *   <li>工具调用上下文丢失(messages with role 'tool'…)→ 友好提示</li>
     *   <li>{@link IAIChatHandler#MODEL_ERROR_MAP} 中的关键字匹配 → 对应中文提示</li>
     *   <li>兜底 → defaultMsg 参数</li>
     * </ol>
     *

View on GitHub (pinned to 96fb33f5ec)

Solutions

  1. Check the server logs at line 635 (log.error) for the full exception stack trace including the cause.
  2. If the underlying cause is the path-traversal exception from line 628, treat it as a security issue and investigate the imageUrl input.
  3. If the cause is IOException, verify the file exists and is readable at the resolved path.
  4. Consider not re-wrapping the JeecgBootException from the security check to preserve the security-specific error message for logging and monitoring.
  5. Verify uploadpath configuration and file existence.

Example fix

// before — catch-all re-wraps security exceptions, losing context
} catch (Exception e) {
    log.error("图片读取失败: {}", imageUrl, e);
    throw new JeecgBootException("图片读取失败: " + imageUrl);
}

// after — preserve security exceptions, wrap only I/O errors
} catch (JeecgBootException e) {
    throw e; // re-throw security violations as-is
} catch (Exception e) {
    log.error("图片读取失败: {}", imageUrl, e);
    throw new JeecgBootException("图片读取失败: " + imageUrl, e);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before reading image, verify existence and accessibility
String filePath = uploadpath + File.separator + imageUrl;
Path targetPath = Paths.get(filePath);
if (!Files.exists(targetPath)) {
    log.warn("Image file not found: {}", filePath);
    // Skip this image or return empty
}
if (!Files.isReadable(targetPath)) {
    log.warn("Image file not readable: {}", filePath);
}
// Verify within upload root
File uploadDir = new File(uploadpath).getCanonicalFile();
File target = new File(filePath).getCanonicalFile();
if (!target.toPath().startsWith(uploadDir.toPath())) {
    throw new JeecgBootException("非法文件路径: " + imageUrl);
}

Type guard

public static boolean isImageFileAccessible(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

// Recommended: preserve the security exception instead of re-wrapping
try {
    // path traversal check
    if (!targetFile.toPath().startsWith(uploadDir.toPath())) {
        throw new JeecgBootException("非法文件路径,禁止访问上传目录之外的文件: " + imageUrl);
    }
    fileContent = Files.readAllBytes(targetFile.toPath());
} catch (JeecgBootException e) {
    // Security violation — re-throw as-is, do NOT re-wrap
    throw e;
} catch (Exception e) {
    log.error("图片读取失败: {}", imageUrl, e);
    throw new JeecgBootException("图片读取失败: " + imageUrl, e);
}

Prevention

When it happens

Trigger: Any exception during local image reading in the image-edit flow: the JeecgBootException from the path traversal check (line 628), IOException from Files.readAllBytes, or other runtime exceptions during file processing. The catch(Exception e) at line 634 catches all of these and throws a generic message.

Common situations: File not found at the expected path. Permission denied reading the file. The path-traversal check throws JeecgBootException which is then re-wrapped here, obscuring the security violation. Corrupted or zero-length image file. Disk I/O errors.

Related errors


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