背景
receptron/mulmoclaude PR #1179 の e2e-live L-04 (animation: true mulmoScript の動画生成) を local で走らせている際、 ホスト負荷が高い状態で Page.captureScreenshot timed out. Increase the 'protocolTimeout' setting in launch/connect calls for a higher timeout if needed. の error が SSE 経由で送出され、 mulmoScript view が永久 spinner になる症状を確認。
Playwright trace に記録された SSE 応答:
```
data: {"type":"beat_image_done","beatIndex":0}
data: {"type":"error","message":"Page.captureScreenshot timed out. Increase the 'protocolTimeout' setting in launch/connect calls for a higher timeout if needed."}
```
現状の問題
`src/utils/html_render.ts` の 4 箇所の `puppeteer.launch` (lines 144 / 210 / 261 / 310) と `src/agents/puppeteer_crawler_agent.ts:36`、 `src/actions/pdf.ts:187` の すべてで `protocolTimeout` を設定していない → Puppeteer default (180s) が効く。
```ts
// 例: src/utils/html_render.ts:210 (renderHTMLToFrames)
const browser = await puppeteer.launch({
args: isCI ? ["--no-sandbox", "--allow-file-access-from-files"] : ["--allow-file-access-from-files"],
});
// ↑ protocolTimeout なし
```
`renderHTMLToFrames` のメインループ (line 227-246) は per-frame で `page.screenshot()` を直列に呼ぶが、 timeout に対する retry が一切無い:
```ts
for (let frame = 0; frame < totalFrames; frame++) {
await page.evaluate(...);
await syncVideosToFrame(...);
await page.screenshot({ path: framePath }); // ← 1 回失敗 = 全体失敗
framePaths.push(framePath);
}
```
env や config から protocolTimeout を override する仕組みも無いため、 重い host (今回は load avg 60+) では user が手元で対処する手段が無い。
影響
- ホスト負荷 / Tailwind CDN 遅延 / docker 内での実行 等で screenshot 1 回が 180s を超えると、 60 frame の loop の途中で error → mulmoScript view 側 (mulmoclaude) は SSE error を捕まえないため永久 spinner (これは別 issue で起票予定: receptron/mulmoclaude 側)
- e2e-live の L-04 が flaky になる
- production user が「動かない」 だけのレポートをして根本原因を特定しにくい
修正案
puppeteer.launch に `protocolTimeout` を明示:
```ts
await puppeteer.launch({
args: [...],
protocolTimeout: PUPPETEER_PROTOCOL_TIMEOUT_MS,
});
```
- env で override 可 (例: `MULMO_PUPPETEER_PROTOCOL_TIMEOUT_MS`、 default 180s 維持)
- `renderHTMLToFrames` の per-frame screenshot に retry を入れる (例: 2-3 回まで 1s sleep + retry、 全 retry 失敗で error 送出)
確認した範囲
- branch: `receptron/mulmocast-cli main` (local clone)
- grep 結果: `protocolTimeout` / `PUPPETEER_` / `MULMO_PUPPET` 等のヒットは無し → 上書き経路無し
- 既存 issue 検索 ("protocolTimeout" in mulmocast-cli): ヒット無し
関連
背景
receptron/mulmoclaude PR #1179 の e2e-live L-04 (
animation: truemulmoScript の動画生成) を local で走らせている際、 ホスト負荷が高い状態でPage.captureScreenshot timed out. Increase the 'protocolTimeout' setting in launch/connect calls for a higher timeout if needed.の error が SSE 経由で送出され、 mulmoScript view が永久 spinner になる症状を確認。Playwright trace に記録された SSE 応答:
```
data: {"type":"beat_image_done","beatIndex":0}
data: {"type":"error","message":"Page.captureScreenshot timed out. Increase the 'protocolTimeout' setting in launch/connect calls for a higher timeout if needed."}
```
現状の問題
`src/utils/html_render.ts` の 4 箇所の `puppeteer.launch` (lines 144 / 210 / 261 / 310) と `src/agents/puppeteer_crawler_agent.ts:36`、 `src/actions/pdf.ts:187` の すべてで `protocolTimeout` を設定していない → Puppeteer default (180s) が効く。
```ts
// 例: src/utils/html_render.ts:210 (renderHTMLToFrames)
const browser = await puppeteer.launch({
args: isCI ? ["--no-sandbox", "--allow-file-access-from-files"] : ["--allow-file-access-from-files"],
});
// ↑ protocolTimeout なし
```
`renderHTMLToFrames` のメインループ (line 227-246) は per-frame で `page.screenshot()` を直列に呼ぶが、 timeout に対する retry が一切無い:
```ts
for (let frame = 0; frame < totalFrames; frame++) {
await page.evaluate(...);
await syncVideosToFrame(...);
await page.screenshot({ path: framePath }); // ← 1 回失敗 = 全体失敗
framePaths.push(framePath);
}
```
env や config から protocolTimeout を override する仕組みも無いため、 重い host (今回は load avg 60+) では user が手元で対処する手段が無い。
影響
修正案
puppeteer.launchに `protocolTimeout` を明示:```ts
await puppeteer.launch({
args: [...],
protocolTimeout: PUPPETEER_PROTOCOL_TIMEOUT_MS,
});
```
確認した範囲
関連