diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 54df2f3a4..043395d74 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -68,6 +68,9 @@ ### Fixed - Fixed `/new` retaining completed or failed async jobs from the prior session in `hub jobs` until the five-minute retention window expired ([#6828](https://github.com/can1357/oh-my-pi/issues/6828)). +### Fixed + +- Fixed `omp update` surfacing a raw `TimeoutError: The operation timed out.` when the 15-minute download deadline fired while streaming the binary body rather than during the initial `fetch()`. The streaming-phase catch now emits the same friendly `Timed out downloading release binary after 15 minutes` message as the connection phase ([#6822](https://github.com/can1357/oh-my-pi/issues/6822)). ## [17.1.6] - 2026-07-27 diff --git a/packages/coding-agent/src/cli/update-cli.ts b/packages/coding-agent/src/cli/update-cli.ts index e86d22782..3b87fc6b0 100644 --- a/packages/coding-agent/src/cli/update-cli.ts +++ b/packages/coding-agent/src/cli/update-cli.ts @@ -231,6 +231,9 @@ export async function downloadVerifiedBinary(options: VerifiedBinaryDownloadOpti await fs.promises.chmod(options.targetPath, 0o755); } catch (err) { await unlinkIfExists(options.targetPath); + if (isTimeoutError(err)) { + throw new Error("Timed out downloading release binary after 15 minutes", { cause: err }); + } throw err; } } diff --git a/packages/coding-agent/test/update-cli.test.ts b/packages/coding-agent/test/update-cli.test.ts index 88df3a3fb..c232253c4 100644 --- a/packages/coding-agent/test/update-cli.test.ts +++ b/packages/coding-agent/test/update-cli.test.ts @@ -465,6 +465,31 @@ describe("update-cli release binary integrity", () => { expect(await Bun.file(targetPath).exists()).toBe(false); }); + it("wraps a timeout during body streaming with a friendly message", async () => { + const dir = await makeTempDir(); + const targetPath = path.join(dir, binaryName); + const body = new ReadableStream( + { + pull(controller) { + controller.enqueue(new Uint8Array(1)); + controller.error(new DOMException("The operation timed out.", "TimeoutError")); + }, + }, + { highWaterMark: 0 }, + ); + + await expect( + downloadVerifiedBinary({ + url, + targetPath, + expectedSize: Buffer.byteLength(content), + expectedDigest: digest, + fetchImpl: async () => new Response(body), + }), + ).rejects.toThrow("Timed out downloading release binary after 15 minutes"); + expect(await Bun.file(targetPath).exists()).toBe(false); + }); + it("removes downloads whose size or digest does not match", async () => { const dir = await makeTempDir(); const targetPath = path.join(dir, binaryName);