|
|
@@ -15,6 +15,13 @@ import type { SystemError } from "bun"
|
|
|
import type { Provider } from "@/provider/provider"
|
|
|
import { ModelID, ProviderID } from "@/provider/schema"
|
|
|
|
|
|
+/** Error shape thrown by Bun's fetch() when gzip/br decompression fails mid-stream */
|
|
|
+interface FetchDecompressionError extends Error {
|
|
|
+ code: "ZlibError"
|
|
|
+ errno: number
|
|
|
+ path: string
|
|
|
+}
|
|
|
+
|
|
|
export namespace MessageV2 {
|
|
|
export function isMedia(mime: string) {
|
|
|
return mime.startsWith("image/") || mime === "application/pdf"
|
|
|
@@ -906,7 +913,10 @@ export namespace MessageV2 {
|
|
|
return result
|
|
|
}
|
|
|
|
|
|
- export function fromError(e: unknown, ctx: { providerID: ProviderID }): NonNullable<Assistant["error"]> {
|
|
|
+ export function fromError(
|
|
|
+ e: unknown,
|
|
|
+ ctx: { providerID: ProviderID; aborted?: boolean },
|
|
|
+ ): NonNullable<Assistant["error"]> {
|
|
|
switch (true) {
|
|
|
case e instanceof DOMException && e.name === "AbortError":
|
|
|
return new MessageV2.AbortedError(
|
|
|
@@ -938,6 +948,21 @@ export namespace MessageV2 {
|
|
|
},
|
|
|
{ cause: e },
|
|
|
).toObject()
|
|
|
+ case e instanceof Error && (e as FetchDecompressionError).code === "ZlibError":
|
|
|
+ if (ctx.aborted) {
|
|
|
+ return new MessageV2.AbortedError({ message: e.message }, { cause: e }).toObject()
|
|
|
+ }
|
|
|
+ return new MessageV2.APIError(
|
|
|
+ {
|
|
|
+ message: "Response decompression failed",
|
|
|
+ isRetryable: true,
|
|
|
+ metadata: {
|
|
|
+ code: (e as FetchDecompressionError).code,
|
|
|
+ message: e.message,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ { cause: e },
|
|
|
+ ).toObject()
|
|
|
case APICallError.isInstance(e):
|
|
|
const parsed = ProviderError.parseAPICallError({
|
|
|
providerID: ctx.providerID,
|