correctness-and-error-handling
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCorrectness & Error Handling Review
正确性与错误处理审查
Review $ARGUMENTS (or the whole app if no argument is given) for correctness issues and missing error handling. Work through every step below and report all findings with file paths and line numbers.
审查**$ARGUMENTS**(若未提供参数则审查整个应用),检查正确性问题与缺失的错误处理。完成以下每一个步骤,并附上文件路径和行号报告所有发现。
Step 1 — Map data flows
步骤1 — 映射数据流
Read these files before checking anything:
- /
src/main.tsx— top-level error boundaries and auth flowsrc/App.tsx - All files matching ,
**/hooks/*.ts— shared async state**/contexts/*.tsx - All files matching ,
**/api/*.ts— CDF SDK call sites**/services/*.ts
For each async data source, note:
- What happens when the request fails (network error, CDF 403, timeout)?
- What does the UI show while loading?
- What does the UI show if the result is empty?
在检查任何内容前,先阅读以下文件:
- /
src/main.tsx— 顶层错误边界与认证流程src/App.tsx - 所有匹配、
**/hooks/*.ts的文件 — 共享异步状态**/contexts/*.tsx - 所有匹配、
**/api/*.ts的文件 — CDF SDK调用位置**/services/*.ts
针对每个异步数据源,记录:
- 请求失败时(网络错误、CDF 403、超时)会发生什么?
- 加载时UI会显示什么?
- 结果为空时UI会显示什么?
Step 2 — Verify top-level error boundaries
步骤2 — 验证顶层错误边界
Every Dune app must have at least one React Error Boundary wrapping the main content so that an unexpected render-time exception shows a user-friendly message instead of a blank screen.
bash
grep -rn --include="*.tsx" --include="*.ts" -E "ErrorBoundary|componentDidCatch|getDerivedStateFromError" src/If no error boundary exists, add one around the main app content:
tsx
import { ErrorBoundary } from "react-error-boundary";
function ErrorFallback({ error }: { error: Error }) {
return (
<div role="alert" className="p-8 text-center">
<p className="text-lg font-semibold">Something went wrong</p>
<pre className="mt-2 text-sm text-muted-foreground">{error.message}</pre>
</div>
);
}
// In App.tsx:
<ErrorBoundary FallbackComponent={ErrorFallback}>
<MainContent />
</ErrorBoundary>每个Dune应用必须至少有一个React Error Boundary包裹主内容,这样意外的渲染时异常会显示友好的用户提示,而非空白屏幕。
bash
grep -rn --include="*.tsx" --include="*.ts" -E "ErrorBoundary|componentDidCatch|getDerivedStateFromError" src/若不存在错误边界,请在主应用内容外添加一个:
tsx
import { ErrorBoundary } from "react-error-boundary";
function ErrorFallback({ error }: { error: Error }) {
return (
<div role="alert" className="p-8 text-center">
<p className="text-lg font-semibold">出现错误</p>
<pre className="mt-2 text-sm text-muted-foreground">{error.message}</pre>
</div>
);
}
// 在App.tsx中:
<ErrorBoundary FallbackComponent={ErrorFallback}>
<MainContent />
</ErrorBoundary>Step 3 — Audit async functions for missing try/catch
步骤3 — 审计缺失try/catch的异步函数
Search for every function and chain that does not have error handling:
asyncPromisebash
undefined搜索所有未处理错误的函数与链:
asyncPromisebash
undefinedFind async functions
查找async函数
grep -rn --include=".ts" --include=".tsx" -E "async\s+function|async\s+(" src/
grep -rn --include=".ts" --include=".tsx" -E "async\s+function|async\s+(" src/
Find .then() without .catch()
查找未搭配.catch()的.then()
grep -rn --include=".ts" --include=".tsx" -E ".then(" src/ | grep -v ".catch("
For each async CDF call, the pattern must be:
```ts
// GOOD — errors are caught and surfaced
async function fetchAssets(sdk: CogniteClient) {
try {
const result = await sdk.assets.list({ limit: 100 });
return result.items;
} catch (error) {
console.error("Failed to fetch assets:", error);
throw error; // re-throw so the caller / query layer can handle it
}
}When using TanStack Query (/), the library catches errors automatically — but verify that and are consumed in the component:
useQueryuseMutationisErrorerrortsx
const { data, isLoading, isError, error } = useQuery({
queryKey: ["assets"],
queryFn: () => fetchAssets(sdk),
});
if (isError) return <ErrorMessage error={error} />; // must be presentgrep -rn --include=".ts" --include=".tsx" -E ".then(" src/ | grep -v ".catch("
针对每个异步CDF调用,必须遵循以下模式:
```ts
// 规范示例 — 错误被捕获并上报
async function fetchAssets(sdk: CogniteClient) {
try {
const result = await sdk.assets.list({ limit: 100 });
return result.items;
} catch (error) {
console.error("获取资产失败:", error);
throw error; // 重新抛出,以便调用者/查询层处理
}
}使用TanStack Query(/)时,库会自动捕获错误——但需验证组件中是否使用了和:
useQueryuseMutationisErrorerrortsx
const { data, isLoading, isError, error } = useQuery({
queryKey: ["assets"],
queryFn: () => fetchAssets(sdk),
});
if (isError) return <ErrorMessage error={error} />; // 必须存在Step 4 — Ensure loading and error states are shown in every component
步骤4 — 确保每个组件都显示加载与错误状态
For each component that fetches data, verify it has three distinct UI states:
| State | Required UI |
|---|---|
| Loading | Spinner, skeleton, or loading indicator |
| Error | User-readable message (not a raw error object or blank space) |
| Empty | "No results" / "Nothing here yet" message (not a blank list) |
Search for components that render data without checking loading state:
bash
grep -rn --include="*.tsx" -E "\.(map|filter|find)\(" src/ | grep -v "isLoading\|isPending\|skeleton\|Skeleton"For each hit, read the component to confirm loading/error/empty states are handled above the call.
.map()针对每个获取数据的组件,验证其具备三种不同的UI状态:
| 状态 | 要求的UI |
|---|---|
| 加载中 | 加载动画、骨架屏或加载指示器 |
| 错误 | 用户可读的提示信息(而非原始错误对象或空白区域) |
| 空数据 | "无结果" / "暂无内容"提示(而非空白列表) |
搜索未检查加载状态就渲染数据的组件:
bash
grep -rn --include="*.tsx" -E "\.(map|filter|find)\(" src/ | grep -v "isLoading\|isPending\|skeleton\|Skeleton"针对每个匹配结果,查看组件确认在调用上方已处理加载/错误/空数据状态。
.map()Step 5 — Narrow types before use
步骤5 — 使用前缩小类型范围
External data (CDF responses, URL params, , ) must be validated before use. TypeScript types alone are not runtime guarantees.
localStorageJSON.parsebash
undefined外部数据(CDF响应、URL参数、、)在使用前必须验证。仅靠TypeScript类型无法提供运行时保障。
localStorageJSON.parsebash
undefinedFind JSON.parse without validation
查找未验证的JSON.parse
grep -rn --include=".ts" --include=".tsx" -E "JSON.parse(" src/
grep -rn --include=".ts" --include=".tsx" -E "JSON.parse(" src/
Find localStorage reads
查找localStorage读取操作
grep -rn --include=".ts" --include=".tsx" -E "localStorage.(get|set)Item" src/
grep -rn --include=".ts" --include=".tsx" -E "localStorage.(get|set)Item" src/
Find useSearchParams usage
查找useSearchParams使用情况
grep -rn --include=".ts" --include=".tsx" -E "useSearchParams|searchParams.get" src/
For each hit, verify the value is either:
- Validated with Zod: `const parsed = MySchema.safeParse(raw);`
- Guarded with a type guard: `if (typeof value !== "string") return;`
- Handled with a nullish fallback: `const id = params.get("id") ?? defaultId;`
Do not cast external data with `as MyType` — that bypasses runtime safety.
---grep -rn --include=".ts" --include=".tsx" -E "useSearchParams|searchParams.get" src/
针对每个匹配结果,验证值满足以下任一条件:
- 使用Zod验证:`const parsed = MySchema.safeParse(raw);`
- 使用类型守卫:`if (typeof value !== "string") return;`
- 提供空值回退:`const id = params.get("id") ?? defaultId;`
请勿使用`as MyType`强制转换外部数据——这会绕过运行时安全检查。
---Step 6 — Handle null, undefined, and empty arrays
步骤6 — 处理null、undefined与空数组
Read every component that accesses properties of data returned from CDF or passed via props.
Common patterns to flag:
tsx
// BAD — crashes if data is undefined
const name = asset.properties.space.Asset.name;
// GOOD — optional chaining
const name = asset.properties?.["my-space"]?.["Asset"]?.name ?? "Unknown";
// BAD — crashes if items is undefined
items.map(renderItem);
// GOOD
(items ?? []).map(renderItem);
// BAD — crashes if the array is empty
const first = items[0].name;
// GOOD
const first = items.at(0)?.name ?? "—";Search for direct array index access:
bash
grep -rn --include="*.tsx" --include="*.ts" -E "\w+\[0\]\." src/查看所有访问CDF返回数据或props传递数据属性的组件。
需标记的常见错误模式:
tsx
// 错误示例 — 若data为undefined会崩溃
const name = asset.properties.space.Asset.name;
// 规范示例 — 可选链操作
const name = asset.properties?.["my-space"]?.["Asset"]?.name ?? "未知";
// 错误示例 — 若items为undefined会崩溃
items.map(renderItem);
// 规范示例
(items ?? []).map(renderItem);
// 错误示例 — 若数组为空会崩溃
const first = items[0].name;
// 规范示例
const first = items.at(0)?.name ?? "—";搜索直接数组索引访问:
bash
grep -rn --include="*.tsx" --include="*.ts" -E "\w+\[0\]\." src/Step 7 — Verify useEffect cleanup
步骤7 — 验证useEffect清理逻辑
Every that sets up a subscription, timer, event listener, or async operation that can outlive the component must return a cleanup function.
useEffectbash
grep -rn --include="*.tsx" --include="*.ts" -B 2 -A 15 "useEffect" src/For each , check:
useEffect| Pattern | Required cleanup |
|---|---|
| |
| |
| CDF streaming / SSE | Close the stream |
| |
| Zustand / event emitter subscription | |
Missing cleanup causes memory leaks and stale state updates on unmounted components.
ts
// GOOD — async fetch with abort
useEffect(() => {
const controller = new AbortController();
async function load() {
try {
const data = await fetchWithSignal(controller.signal);
if (!controller.signal.aborted) setState(data);
} catch (err) {
if (err instanceof Error && err.name !== "AbortError") {
setError(err);
}
}
}
load();
return () => controller.abort();
}, [id]);每个设置了订阅、定时器、事件监听器或可能超出组件生命周期的异步操作的,必须返回清理函数。
useEffectbash
grep -rn --include="*.tsx" --include="*.ts" -B 2 -A 15 "useEffect" src/针对每个,检查:
useEffect| 模式 | 要求的清理操作 |
|---|---|
| |
| |
| CDF流 / SSE | 关闭流 |
| |
| Zustand / 事件发射器订阅 | |
缺失清理会导致内存泄漏,以及对已卸载组件进行过时状态更新。
ts
// 规范示例 — 带中止功能的异步请求
useEffect(() => {
const controller = new AbortController();
async function load() {
try {
const data = await fetchWithSignal(controller.signal);
if (!controller.signal.aborted) setState(data);
} catch (err) {
if (err instanceof Error && err.name !== "AbortError") {
setError(err);
}
}
}
load();
return () => controller.abort();
}, [id]);Step 8 — Check edge cases
步骤8 — 检查边缘场景
For each feature, verify behaviour for:
- Empty data: zero-item lists, zero-result CDF queries
- Single item: list with exactly one entry (common source of off-by-one rendering bugs)
- Maximum data: what happens when CDF returns the full and there are more pages? Is pagination communicated?
limit - Concurrent requests: if the user triggers a new search before the previous completes, is the stale result discarded?
- Network offline: does the app show a meaningful message or silently fail?
For Atlas tool functions, verify every field is present and within expected bounds before calling CDF:
executeargsts
execute: async (args) => {
if (!args.assetId || typeof args.assetId !== "string") {
return { output: "Missing or invalid assetId", details: null };
}
// ... safe to proceed
}针对每个功能,验证以下场景的行为:
- 空数据:零条目列表、CDF查询无结果
- 单条目:列表仅含一个条目(常见的差一渲染Bug来源)
- 最大数据量:当CDF返回满结果且存在更多分页时,会发生什么?是否告知用户分页信息?
limit - 并发请求:若用户在上一个请求完成前触发新搜索,是否会丢弃过时结果?
- 网络离线:应用是否显示有意义的提示,还是静默失败?
对于Atlas工具的函数,验证调用CDF前每个字段都存在且在预期范围内:
executeargsts
execute: async (args) => {
if (!args.assetId || typeof args.assetId !== "string") {
return { output: "缺失或无效的assetId", details: null };
}
// ... 可安全执行后续操作
}Step 9 — Report findings
步骤9 — 报告发现
Produce a structured report:
| Severity | File | Line | Issue | Recommendation |
|---|---|---|---|---|
| HIGH | | 34 | Unhandled promise rejection in fetchAssets | Wrap in try/catch and surface error state |
| MEDIUM | | 12 | No empty state rendered when | Add empty state message |
| LOW | | — | No top-level ErrorBoundary | Wrap |
If no issues are found in a step, state "No issues found" for that step. Do not skip steps silently.
生成结构化报告:
| 严重程度 | 文件 | 行号 | 问题 | 建议 |
|---|---|---|---|---|
| 高 | | 34 | fetchAssets中存在未处理的Promise拒绝 | 用try/catch包裹并上报错误状态 |
| 中 | | 12 | 当 | 添加空状态提示信息 |
| 低 | | — | 无顶层ErrorBoundary | 用ErrorBoundary包裹 |
若某步骤未发现问题,需注明“未发现问题”,请勿静默跳过步骤。
Done
完成
Summarize findings by severity. Flag any HIGH issues that could cause data loss, crashes in production, or misleading UI states, and list them first for immediate attention.
按严重程度总结发现。标记可能导致数据丢失、生产环境崩溃或误导性UI状态的高优先级问题,并将其列为首要处理项。