快速结论:此问题发生在 Open WebUI 工作区 Tool 生成文件(PDF/DOCX/XLSX 等)后,聊天界面没有显示可点击下载的原生附件,或附件点击后只打开预览/无响应。优先排查文件是否已通过 Tool 注册到 Files 面板,以及浏览器端是否存在 JWT 认证导致的 401 下载失败。
适用环境:Open WebUI 0.10.2(Issue 作者已验证);涉及工作区 Tool、Files 面板、聊天附件渲染和 `/api/v1/files/{id}/content` 文件下载接口。
最快修复方案:暂无确认的一步修复方案。Issue 提出了完整的前后端修改方案(后端中间件 `process_tool_result()` 处理 Tool 返回的 `files` 字段并触发原生附件事件,前端 `FileItem.svelte` 改为带 `Authorization: Bearer` 头下载),但截至 Issue 关闭时尚未合并为官方补丁。
注意事项:方案只在 0.10.2 上端到端验证过;自动附加回退依赖工具返回 `generated` 标签(30 秒内、最多 3 个文件),属于约定式行为而非强制 API;前端对 `http://` 外部 URL 不附带 token,只对 Open WebUI 自身文件 API 使用认证下载。
问题场景
用户在 Open WebUI 的 Workspace 中配置了自定义 Tool,该 Tool 运行后生成了文件(如 PDF、DOCX、XLSX)。文件虽然出现在 Files 面板中,但聊天窗口没有生成可点击下载的原生附件;或者附件缩略图点击后打开预览弹窗,而不是直接下载文件。Issue 作者还指出,直接在浏览器地址栏访问 `/api/v1/files/{id}/content` 会返回 401,因为 Open WebUI 的 JWT 存储在 `localStorage.token` 中,普通浏览器导航不会携带 `Authorization` 头。
报错原文
feat: Tool-generated files as native in-chat download attachments (click = download) — with working solution
Navigating the browser to /api/v1/files/{id}/content returns 401:
OpenWebUI stores the JWT in localStorage.token and only sends it as an
Authorization: Bearer header on fetch. A browser navigation sends no header,
and the httponly token cookie is absent/expired in many sessions and blocked
in iframe/cross-site embeds.
原因分析
可能原因有两点:
一是后端没有把 Tool 返回的文件注册为聊天原生附件。当前 Tool 运行后文件虽然进入了 Files 面板,但聊天事件流中没有触发附件渲染所需的事件,导致前端无法显示可下载的附件块。
二是前端附件点击行为有缺陷。`FileItem.svelte` 对普通文件引用默认打开预览弹窗,而下载动作依赖浏览器直接导航到文件 URL;Open WebUI 的 JWT 不会通过浏览器导航自动附加,导致下载请求被 401 拒绝。
环境排查
- 确认 Open WebUI 版本是否为 0.10.2 或相近版本(Issue 在此版本验证通过)。
- 检查 Tool 返回值是否包含 `files` 字段(`[{id, filename, content_type, size}]` 结构)。
- 确认 Tool 生成的文件是否已出现在 Files 面板中(如果文件本身未注册,问题不在附件渲染层)。
- 确认浏览器开发者工具 Network 面板中,下载请求是否带有 `Authorization: Bearer ` 头(从 `localStorage.token` 读取)。
- 确认当前会话的 JWT 是否存在且未过期;检查 `localStorage.token` 是否为空。
- 如果使用 iframe 或跨站嵌入,确认 httponly `token` cookie 是否被浏览器阻止。
解决步骤
- 后端改造(open_webui/utils/middleware.py):修改
process_tool_result(),使其接受 Tool 返回的{"output", "files": [{id, filename, content_type, size, ...}]}结构,将文件列表注册为聊天原生附件;同时为只返回文本的 Tool 增加自动附加回退逻辑——运行后 30 秒内、带generated标签的文件(最多 3 个)自动附加。 - 前端改造(src/lib/components/common/FileItem.svelte):修改点击处理逻辑——对于普通文件引用跳过预览弹窗,改用
fetch(url, {headers: {Authorization: Bearer <localStorage.token>}})获取 blob,再通过<a download="filename">触发下载;请求失败时回退为普通导航;对外部http://URL 不附带 token。 - 附件参数追加:在下载请求 URL 上追加
?attachment=true——后端GET /api/v1/files/{id}/content已支持该参数,会返回Content-Disposition: attachment头。 - 事件触发:在原生 Tool 文件路径上触发
files事件,确保即使模型最终回复为空,附件块也能正常显示。
说明:以上步骤来自 Issue 作者提出的完整方案,是可以在 0.10.2 上端到端验证的;但尚未合并到官方代码,需自行打补丁或等待后续版本。可优先尝试第 1 和第 2 步组合。
验证方法
确认问题已解决的标准:在 Workspace 配置一个生成文件的 Tool,运行后聊天窗口出现文件附件块;点击附件直接触发浏览器下载(不打开预览弹窗);下载的文件内容正确且无 401 错误。也可在浏览器开发者工具 Network 面板中确认下载请求带有 `Authorization: Bearer` 头并返回 200。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


