From 708e4d8867f68bcddbfa2455a254260ee52bc439 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" <219766164+opencode-agent[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2026 09:23:10 +0000 Subject: [PATCH] fix(app): refine mobile error page (#44353) --- packages/app/src/runtime/i18n/en.ts | 3 +++ .../app/src/shell/errors/description.test.ts | 18 ++++++++++++- packages/app/src/shell/errors/description.ts | 18 +++++++++++++ packages/app/src/shell/errors/error.tsx | 25 ++++++++++++------- 4 files changed, 54 insertions(+), 10 deletions(-) diff --git a/packages/app/src/runtime/i18n/en.ts b/packages/app/src/runtime/i18n/en.ts index ddb1926c435..15c0c425602 100644 --- a/packages/app/src/runtime/i18n/en.ts +++ b/packages/app/src/runtime/i18n/en.ts @@ -570,10 +570,13 @@ export const dict = { "toast.update.action.notYet": "Not yet", "error.page.title": "Something went wrong", + "error.page.title.status": "Server request failed", "error.page.description": "An error occurred while loading the application.", + "error.page.description.status": "The server returned an HTTP {{status}} response.", "error.page.description.localServerStartup": "An error occurred while starting the local server.", "error.page.details.label": "Error Details", "error.page.action.restart": "Restart", + "error.page.action.reload": "Reload", "error.page.action.report": "Report Error", "error.page.action.reported": "Error Reported", "error.page.action.exportLogs": "Export Logs", diff --git a/packages/app/src/shell/errors/description.test.ts b/packages/app/src/shell/errors/description.test.ts index 7478abdf2e5..f3c36363599 100644 --- a/packages/app/src/shell/errors/description.test.ts +++ b/packages/app/src/shell/errors/description.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import { errorDescriptionKey } from "./description" +import { errorDescriptionKey, errorStatus } from "./description" describe("error description", () => { test("describes local server startup errors", () => { @@ -15,3 +15,19 @@ describe("error description", () => { ) }) }) + +describe("error status", () => { + test("finds status codes in an error cause", () => { + expect(errorStatus(new Error("UnexpectedStatus", { cause: { status: 502 } }))).toBe(502) + }) + + test("finds status codes in structured error data", () => { + expect(errorStatus({ name: "APIError", data: { statusCode: 401 } })).toBe(401) + }) + + test("ignores invalid and circular status values", () => { + const error: { status: number; cause?: unknown } = { status: 99 } + error.cause = error + expect(errorStatus(error)).toBeUndefined() + }) +}) diff --git a/packages/app/src/shell/errors/description.ts b/packages/app/src/shell/errors/description.ts index 86accb6e1df..adeee324f13 100644 --- a/packages/app/src/shell/errors/description.ts +++ b/packages/app/src/shell/errors/description.ts @@ -9,3 +9,21 @@ export function errorDescriptionKey(error: unknown) { } return "error.page.description" as const } + +export function errorStatus(error: unknown) { + const seen = new Set() + const visit = (value: unknown): number | undefined => { + if (typeof value !== "object" || value === null || seen.has(value)) return + seen.add(value) + const item = value as Record + + for (const key of ["status", "statusCode"] as const) { + const status = item[key] + if (typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599) return status + } + + return visit(item.cause) ?? visit(item.data) + } + + return visit(error) +} diff --git a/packages/app/src/shell/errors/error.tsx b/packages/app/src/shell/errors/error.tsx index 968063c97ae..883d9ae94bb 100644 --- a/packages/app/src/shell/errors/error.tsx +++ b/packages/app/src/shell/errors/error.tsx @@ -7,7 +7,7 @@ import { createStore } from "solid-js/store" import { usePlatform } from "@/runtime/platform/platform" import { useLanguage } from "@/runtime/i18n/language" import { Icon } from "@opencode-ai/ui/icon" -import { errorDescriptionKey } from "./description" +import { errorDescriptionKey, errorStatus } from "./description" export type InitError = { name: string @@ -223,6 +223,7 @@ export const ErrorPage: Component = (props) => { const platform = usePlatform() const language = useLanguage() const formattedError = () => formatError(props.error, language.t) + const status = () => errorStatus(props.error) let recordedFatalError: Promise | undefined const [store, setStore] = createStore({ actionError: undefined as string | undefined, @@ -277,14 +278,20 @@ export const ErrorPage: Component = (props) => { return (
-
- +
+
-

{language.t("error.page.title")}

-

{language.t(errorDescriptionKey(props.error))}

+

+ {language.t(status() ? "error.page.title.status" : "error.page.title")} +

+

+ {status() + ? language.t("error.page.description.status", { status: status()! }) + : language.t(errorDescriptionKey(props.error))} +

= (props) => { />