correctness-and-error-handling

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Correctness & 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
    /
    src/App.tsx
    — top-level error boundaries and auth flow
  • All files matching
    **/hooks/*.ts
    ,
    **/contexts/*.tsx
    — shared async state
  • All files matching
    **/api/*.ts
    ,
    **/services/*.ts
    — CDF SDK call sites
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
    **/services/*.ts
    的文件 — CDF SDK调用位置
针对每个异步数据源,记录:
  • 请求失败时(网络错误、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
async
function and
Promise
chain that does not have error handling:
bash
undefined
搜索所有未处理错误的
async
函数与
Promise
链:
bash
undefined

Find 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 (
useQuery
/
useMutation
), the library catches errors automatically — but verify that
isError
and
error
are consumed in the component:
tsx
const { data, isLoading, isError, error } = useQuery({
  queryKey: ["assets"],
  queryFn: () => fetchAssets(sdk),
});

if (isError) return <ErrorMessage error={error} />;  // must be present

grep -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(
useQuery
/
useMutation
)时,库会自动捕获错误——但需验证组件中是否使用了
isError
error
tsx
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:
StateRequired UI
LoadingSpinner, skeleton, or loading indicator
ErrorUser-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
.map()
call.

针对每个获取数据的组件,验证其具备三种不同的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,
localStorage
,
JSON.parse
) must be validated before use. TypeScript types alone are not runtime guarantees.
bash
undefined
外部数据(CDF响应、URL参数、
localStorage
JSON.parse
)在使用前必须验证。仅靠TypeScript类型无法提供运行时保障。
bash
undefined

Find 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
useEffect
that sets up a subscription, timer, event listener, or async operation that can outlive the component must return a cleanup function.
bash
grep -rn --include="*.tsx" --include="*.ts" -B 2 -A 15 "useEffect" src/
For each
useEffect
, check:
PatternRequired cleanup
addEventListener
removeEventListener
setInterval
/
setTimeout
clearInterval
/
clearTimeout
CDF streaming / SSEClose the stream
fetch
/ CDF SDK call
AbortController.abort()
Zustand / event emitter subscription
unsubscribe()
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]);

每个设置了订阅、定时器、事件监听器或可能超出组件生命周期的异步操作的
useEffect
,必须返回清理函数。
bash
grep -rn --include="*.tsx" --include="*.ts" -B 2 -A 15 "useEffect" src/
针对每个
useEffect
,检查:
模式要求的清理操作
addEventListener
removeEventListener
setInterval
/
setTimeout
clearInterval
/
clearTimeout
CDF流 / SSE关闭流
fetch
/ CDF SDK调用
AbortController.abort()
Zustand / 事件发射器订阅
unsubscribe()
缺失清理会导致内存泄漏,以及对已卸载组件进行过时状态更新。
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
    limit
    and there are more pages? Is pagination communicated?
  • 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
execute
functions, verify every
args
field is present and within expected bounds before calling CDF:
ts
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工具的
execute
函数,验证调用CDF前每个
args
字段都存在且在预期范围内:
ts
execute: async (args) => {
  if (!args.assetId || typeof args.assetId !== "string") {
    return { output: "缺失或无效的assetId", details: null };
  }
  // ... 可安全执行后续操作
}

Step 9 — Report findings

步骤9 — 报告发现

Produce a structured report:
SeverityFileLineIssueRecommendation
HIGH
src/hooks/useAssets.ts
34Unhandled promise rejection in fetchAssetsWrap in try/catch and surface error state
MEDIUM
src/components/AssetList.tsx
12No empty state rendered when
items.length === 0
Add empty state message
LOW
src/App.tsx
No top-level ErrorBoundaryWrap
<App>
content with ErrorBoundary
If no issues are found in a step, state "No issues found" for that step. Do not skip steps silently.

生成结构化报告:
严重程度文件行号问题建议
src/hooks/useAssets.ts
34fetchAssets中存在未处理的Promise拒绝用try/catch包裹并上报错误状态
src/components/AssetList.tsx
12
items.length === 0
时未渲染空状态
添加空状态提示信息
src/App.tsx
无顶层ErrorBoundary用ErrorBoundary包裹
<App>
内容
若某步骤未发现问题,需注明“未发现问题”,请勿静默跳过步骤。

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状态的高优先级问题,并将其列为首要处理项。