---
date: 2026-05-11
type: error
tags: [integration-test, robot-manager, attachment, flake, uat-runner]
---

# robot-manager attachment download 集成测试 500（UAT runner 上稳定复现）

## 现场

issue #260 方案 3（batch by module）落地过程中跑全量集成测试，
12 个 module 11 通过，**robot-manager** 单测失败：

```
FAIL backend/integration/robot-manager/robot-manager.integration.test.ts (59.193s)
  ● Robot Manager API Integration Tests (v3 — metadata-only)
    › Attachments
    › GET /attachments/:attachmentId/download 下载文件
    expected 200 "OK", got 500 "Internal Server Error"

Test Suites: 1 failed, 1 total
Tests:       1 failed, 127 passed, 128 total
```

后端日志：
```
ERROR [AllExceptionsFilter]
GET /api/v1/robot-manager/attachments/<uuid>/download - 500 - INTERNAL_SERVER_ERROR
  at RobotAttachmentController.download
    (backend/src/modules/robot-manager/robot-attachment.controller.ts:64:9)
```

## 排除 #260 batch 的可能

为了确认是不是 batch 改动引入，做了对照实验：

| 跑法 | 命令 | 结果 |
|---|---|---|
| #260 batch 模式 | `bash run-backend-integration.sh --force-reset`（无路径，走 batch） | 1 failed |
| #260 single 透传 | `bash run-backend-integration.sh backend/integration/robot-manager` | 1 failed |
| develop 等效 | `--force-reset` + 手动 `reset_test_db_schema` + jest `--runInBand`，**unset NODE_OPTIONS** | 1 failed |

三种跑法，fresh schema、单进程、单 worker、heap 足够——**结论：与 #260 改动无关**，
也不依赖于"全量跑导致状态污染"。robot-manager 在 develop 上独立跑也是这个症状。

## 直接原因（未深挖）

`robot-attachment.controller.ts:64`：

```typescript
const { att, absPath } = await this.service.getForDownload(attachmentId);
res.setHeader('Content-Type', att.mimeType);
res.setHeader('Content-Disposition', ...);
res.sendFile(absPath);          // ← line 64
```

`res.sendFile(absPath)` 抛 500，绝大概率：
1. `absPath` 在测试环境不存在（上传 mock 没真落盘 / uploads dir 配置错）
2. uploads 根目录的进程权限问题
3. v6 重构后 attachment 持久化路径变了，测试没跟上

git log 显示 `robot-attachment.controller.ts` 最近改动是
`586e5675 feat(robot-manager): v6 全量重构` 和
`f6ff0fe8 feat(robot-manager): Excel 导入...`，都比较老，但这两个 PR
当时跑的 CI（runInBand 串行 32 个 suite 触 OOM 在第 32 个挂掉）**根本没跑到
robot-manager**——按字母序 robot-manager 在 organization 之后；OOM 在第
32 个 suite 触发，robot-manager 是更后面的。

**也就是说，这个 flake 可能从 v6 重构起就存在，被 OOM 遮住了**。#260 落地
让全量真正能跑完才暴露出来。

## 接下来（已完成）

后续调查在 ERR-20260511-002 定位根因：是 `express.sendFile` 底层 `send` 库
拒绝绝对路径里任何 `/.xxx/` 段（agent-pool slot 路径含 `/.agent-pool/`）。
不是 robot-manager 业务问题，是宿主路径与 send 库默认安全策略的冲突。
#260 PR 一并修了 controller 改 stream pipe 绕过。

## 适用范围

- 任何在 CI 上被 OOM / hang / panic 遮住的下游测试，等"上游"修好后会浮出。
  做"扩展可见性"类改动时，要预期会发现一批历史 flake。
- 不要把这种"暴露的 bug"算成自己 PR 的 regression——区分"引入"和"暴露"。

## 关联

- issue: #260
- PR: 待开（fix/integration-batch-by-module-260）
- 受影响 controller: backend/src/modules/robot-manager/robot-attachment.controller.ts:55-65
