From 2ec993dd64c89e64af42bf2ddd5300d3554a24e9 Mon Sep 17 00:00:00 2001 From: Hamed Shams Date: Fri, 9 Oct 2026 00:52:24 +0300 Subject: [PATCH 1/2] A hub that cannot be reached is a formatted error, not a raw TypeError MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bend f.bend with a 0x… hash import and an unreachable BEND_HUB printed Bun's exception class as the whole error. The name@version path already formats the same condition, and hub_ask already words it as "could not be reached", so book_err now recognises a failed hub fetch (Bun's fetch rejects with a TypeError carrying a code) and prints the same line. --- bend2/main.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bend2/main.ts b/bend2/main.ts index 74aada1078..30fa7d0ef9 100755 --- a/bend2/main.ts +++ b/bend2/main.ts @@ -884,6 +884,10 @@ function book_err(e: unknown): string { return e.errors.map((m) => Bun.inspect(m, { colors: false })).join("\n") || String(e); } + // a hub fetch that could not connect: Bun's TypeError carries a code + if (e instanceof TypeError && typeof (e as { code?: unknown }).code === "string") { + return "Error: " + Bend.BEND_HUB + " could not be reached"; + } const err = e as Bend.Err; if (e instanceof RangeError) { return "Error: the machine stack overflowed (a deep recursion, or a" From c1148bf5b90c9d1465d8efa8abb0f4fe497dce96 Mon Sep 17 00:00:00 2001 From: Lorenzobattistela Date: Fri, 9 Oct 2026 06:43:57 -0300 Subject: [PATCH 2/2] Report an unreachable hub at the import, with a timeout (#1401) --- bend2/bend.ts | 10 +++++----- bend2/main.ts | 4 ---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/bend2/bend.ts b/bend2/bend.ts index 0a0814a054..873e5c9c23 100644 --- a/bend2/bend.ts +++ b/bend2/bend.ts @@ -901,10 +901,10 @@ export const BEND_HUB = process.env.BEND_HUB ?? "https://hub.bend-lang.com"; export const NAMED = /^([a-z][a-z0-9-]{0,63})@((?:0|[1-9][0-9]*)(?:\.(?:0|[1-9][0-9]*)){3})$/; async function hub_get(book: Book, sub: string, hash: string, spn?: Span): Promise { - const res = await fetch(BEND_HUB + "/" + sub); - const src = res.ok ? await res.text() : ""; + const res = await fetch(BEND_HUB + "/" + sub, { signal: AbortSignal.timeout(30000) }).catch(() => null); + const src = res?.ok ? await res.text().catch(() => "") : ""; const sum = Buffer.from(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(src))).toString("hex"); - if (!res.ok || hash.length < 32 || sum.slice(0, hash.length) !== hash || path.posix.normalize("/" + sub) !== "/" + sub) { + if (!res?.ok || hash.length < 32 || sum.slice(0, hash.length) !== hash || path.posix.normalize("/" + sub) !== "/" + sub) { throw Err(book, ctx_nil(), "a file at " + BEND_HUB + "/" + sub + " hashing to " + hash, undefined, spn); } return src; @@ -919,8 +919,8 @@ async function name_hash(book: Book, nv: string, spn?: Span): Promise { if (/^0x[0-9a-f]{32}$/.test(old)) { return old; } - const res = await fetch(BEND_HUB + "/name/" + nv).catch(() => null); - const got = res?.ok ? (await res.text()).trim() : ""; + const res = await fetch(BEND_HUB + "/name/" + nv, { signal: AbortSignal.timeout(30000) }).catch(() => null); + const got = res?.ok ? (await res.text().catch(() => "")).trim() : ""; if (!/^0x[0-9a-f]{32}$/.test(got)) { throw Err(book, ctx_nil(), "a package named " + nv + " on " + BEND_HUB + (res?.status === 410 ? " (it was taken down)" : ""), undefined, spn); diff --git a/bend2/main.ts b/bend2/main.ts index 30fa7d0ef9..74aada1078 100755 --- a/bend2/main.ts +++ b/bend2/main.ts @@ -884,10 +884,6 @@ function book_err(e: unknown): string { return e.errors.map((m) => Bun.inspect(m, { colors: false })).join("\n") || String(e); } - // a hub fetch that could not connect: Bun's TypeError carries a code - if (e instanceof TypeError && typeof (e as { code?: unknown }).code === "string") { - return "Error: " + Bend.BEND_HUB + " could not be reached"; - } const err = e as Bend.Err; if (e instanceof RangeError) { return "Error: the machine stack overflowed (a deep recursion, or a"