[Bug]: GET /api/v1/models/all-models returns null rows or panics when a page straddles the end, and page=1 skips the first page

该报错通常出现在调用 RAGFlow 的模型目录分页接口( GET /api/v1/models/all-models 或其管理端副本 GET /api/v1/admin/all-models )并传入 page / page_size 参数时,分页切片上界未按实际长度裁剪,且页码按 0 起始偏移处

快速结论:该报错通常出现在调用 RAGFlow 的模型目录分页接口(GET /api/v1/models/all-models 或其管理端副本 GET /api/v1/admin/all-models)并传入 page/page_size 参数时,分页切片上界未按实际长度裁剪,且页码按 0 起始偏移处理。优先排查:请求页码是否从 1 开始、是否落在最后一页跨越末尾的位置,以及服务端是否已包含对应修复。

适用环境:Issue 讨论中确认的对象为 RAGFlow 后端(Go 实现,涉及 internal/service/model_service.go、internal/admin/service.go、internal/cli/cli.go、internal/common),模型目录规模在测试探针中为 len=3846 cap=4095。Issue 未提供操作系统、Python、CUDA、显卡或依赖版本信息,此处不作补充。

最快修复方案:升级到已合并 #22036 与 #22059 的版本;修复后 ListAllModels 经由 common.ValidatePagination 校验分页参数,并用 min(start+pageSize, len(models)) 裁剪切片上界。

注意事项:客户端若按 0 起始页码约定传 page,在修复版本上需改为 1 起始;不传 page 时行为为返回第一页。修复对应的测试分布在 ./internal/common/、./internal/service/、./internal/admin/、./internal/handler/、./internal/cli/,若自行打补丁需同步覆盖这些包。

问题场景

用户在 RAGFlow 中通过 CLI 或直接请求 REST 接口列出全部模型,并带有 page 与 page_size 分页参数。触发场景有三类:一是请求的页窗口跨越模型目录末尾(page*pageSize < len(models) < (page+1)*pageSize);二是模型切片容量紧凑,同一表达式直接 panic;三是按 CLI 文档以 1 起始传 page=1,但服务端以 0 起始偏移处理,导致第一页不可达。

报错原文

[Bug]: GET /api/v1/models/all-models returns null rows or panics when a page straddles the end, and page=1 skips the first page

slice bounds out of range

len=3846 cap=4095
page 128 size 30 -> 30 items, 24 nil

page 1 returned 10 models starting at aion-labs/aion-1.0-mini, want 302..aion-labs/aion-1.0

原因分析

最可能的原因:ModelProviderService.ListAllModels 与其管理端重复实现中的分页逻辑只校验了下界,未将上界限制为 len(models):

if pageSize > 0 && pageIndex >= 0 && pageIndex*pageSize < len(models) {
	return models[pageIndex*pageSize : (pageIndex+1)*pageSize], nil
}
return models, nil

Go 对切片的双下标表达式只按容量检查上界,因此当页窗口越过末尾但仍在容量内时,会读到数据之后的空位,返回若干 null 条目;容量紧凑时会因上界超出容量而 panic,请求表现为 500 或连接中断。此外 pageIndex 被直接当作 0 起始偏移,而 CLI 帮助文本声明该参数为 1 起始(“Page number (1-based)”),于是 page=1 实际返回第二页。当 page*pageSize >= len(models) 时该分支被跳过,处理器返回完整目录,越界页因此静默忽略分页。

环境排查

  • 确认 RAGFlow 版本是否已包含 #22036 与 #22059 的合并内容,尤其检查 internal/service/model_service.go、internal/admin/service.go 中的分页表达式。
  • 确认调用方(CLI 或自建脚本)使用的 page 是 1 起始还是 0 起始。
  • 确认请求中是否同时携带 page 与 page_size;缺省或 page=0 的语义是返回全部,不属于分页路径。
  • 确认模型目录总条数与 page_size 是否构成跨越末尾的页窗口,可用 page*page_size 与总条数比较判断。
  • Issue 未说明操作系统、Python、CUDA、PyTorch、显卡或前端 WebUI 版本,这些项目与本问题无直接关联证据,无需作为排查前提。

解决步骤

  1. 升级到包含 #22036 与 #22059 的 RAGFlow 版本;这两个 PR 已将 ListAllModels 改为统一走 common.ValidatePagination。
  2. 确认修复后的切片上界为 min(start+pageSize, len(models)),即最后一页只返回剩余记录,跨越末尾的页不会再读到 len 之外的元素。
  3. 确认页码语义为 1 起始;若客户端此前按 0 起始传参,同步将首个请求页码调整为 1。
  4. 确认越界页(起始偏移大于等于总条数)返回空列表,而非整个目录、null 行或 panic。
  5. 如自行维护分支,可将原本位于 agent 列表接口的 slicePage 辅助函数抽到 internal/common,供服务端与管理端复用,避免两处重复的算术逻辑再次分叉。
  6. 可优先尝试:运行 Issue 中列出的包测试 ./internal/common/、./internal/service/、./internal/admin/、./internal/handler/、./internal/cli/,其中新增用例覆盖容量紧凑切片不越界、第一页从首个模型开始、末页只返回余数、越界页为空。

验证方法

分别请求 page=1&page_size=N,确认返回结果从目录中的第一条模型开始而非第二条;请求最后一页,确认返回条数等于总条数除以 page_size 的余数,且响应中不含 null 行;请求起始偏移超过总条数的页,确认返回空列表而非完整目录或 500;对整个模型目录逐页遍历,确认各页记录不重叠、不缺失。

参考来源

infiniflow/ragflow #21727(修复 PR:#22036、#22059,已合并)

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 33552

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注