Q181: a render error stays inside its page and says what failed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-09 05:56:11 -04:00
1 parent fd91832bc4
commit 3039835644
5 files changed
+98 -3

No files matched your search

+3 -2
View File
@@ -1,6 +1,7 @@
import { ErrorBoundary } from "../shared/ErrorBoundary";
import { StartupScreen } from "./StartupScreen";
import type { Focus } from "../pages/types";
import { Fragment, useEffect, useMemo, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { Shell, type RouteId } from "../shared/Shell";
import { AvatarDirectoryContext, type AvatarLook } from "../shared/ui";
import { Onboarding } from "./Onboarding";
@@ -105,7 +106,7 @@ export function App() {
onDeleteOrg={(orgId) => void actions.run(() => window.agentCompany.deleteOrganization({ orgId }))}
onNewOrg={() => setCreatingOrg(true)}
>
<Fragment key={pageKey}>{page}</Fragment>
<ErrorBoundary key={pageKey} scope="page">{page}</ErrorBoundary>
</Shell>
</AvatarDirectoryContext.Provider>
);
+6
View File
@@ -695,6 +695,12 @@ export const zh: Record<string, string> = {
"Unsaved": "未保存",
"Working language": "工作语言",
"That did not work": "操作未成功",
"This page could not be shown.": "此页面无法显示。",
"AgentCompany hit an error it could not recover from here.": "AgentCompany 遇到了无法在此恢复的错误。",
"The rest of the app still works; go to another page or try this one again.": "应用的其他部分仍可使用;可以前往其他页面,或重试此页面。",
"Reloading the window starts it again; your data is kept.": "重新加载窗口即可重新开始;你的数据会保留。",
"Try again": "重试",
"Reload": "重新加载",
"AgentCompany could not load its data.": "AgentCompany 无法加载数据。",
"Retrying every few seconds (attempt {count}).": "每隔几秒重试一次(第 {count} 次)。",
"Retry now": "立即重试",
+4 -1
View File
@@ -2,6 +2,7 @@ import React from "react";
import ReactDOM from "react-dom/client";
import { App } from "./app/App";
import { FailureToasts } from "./shared/FailureToasts";
import { ErrorBoundary } from "./shared/ErrorBoundary";
import "./styles/fonts.css";
import "./styles.css";
import "./styles/components.css";
@@ -9,7 +10,9 @@ import "./overlays.css";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<App />
<ErrorBoundary scope="app">
<App />
</ErrorBoundary>
<FailureToasts />
</React.StrictMode>,
);
+36
View File
@@ -0,0 +1,36 @@
import { Component, type ErrorInfo, type ReactNode } from "react";
import { Button } from "./ui";
import { t } from "../i18n/i18n";
/** Contains a render error to the part of the app that threw, says what failed, and offers a way back; the error is logged with its component stack. */
export class ErrorBoundary extends Component<{ scope: "page" | "app"; children: ReactNode }, { error: Error | null }> {
state: { error: Error | null } = { error: null };
static getDerivedStateFromError(error: Error): { error: Error } {
return { error };
}
componentDidCatch(error: Error, info: ErrorInfo): void {
console.error(`[render] ${this.props.scope} failed: ${error.message}`, info.componentStack);
}
render(): ReactNode {
const { error } = this.state;
if (!error) return this.props.children;
const page = this.props.scope === "page";
return (
<div role="alert" style={{ padding: 24, display: "grid", gap: 10, maxWidth: 560 }}>
<strong>{page ? t("This page could not be shown.") : t("AgentCompany hit an error it could not recover from here.")}</strong>
<div className="mono" style={{ fontSize: 12, color: "var(--text-2)", wordBreak: "break-word" }}>{error.message}</div>
<div style={{ fontSize: 13, color: "var(--text-3)" }}>
{page ? t("The rest of the app still works; go to another page or try this one again.") : t("Reloading the window starts it again; your data is kept.")}
</div>
<div>
{page
? <Button onClick={() => this.setState({ error: null })}>{t("Try again")}</Button>
: <Button onClick={() => window.location.reload()}>{t("Reload")}</Button>}
</div>
</div>
);
}
}