Loading...
Loading...
MUST be used whenever reviewing a Dune app for bugs, missing error states, unhandled promise rejections, or incorrect edge-case behaviour. Do NOT skip — run every step when the user asks for a correctness review, bug check, error handling audit, or robustness review. Triggers: correctness, error handling, bug, edge case, crash, unhandled, null, undefined, empty state, loading state, error boundary, try catch, async error, useEffect cleanup, type guard, runtime error, robustness.
npx skill4agent add cognitedata/dune-skills correctness-and-error-handlingsrc/main.tsxsrc/App.tsx**/hooks/*.ts**/contexts/*.tsx**/api/*.ts**/services/*.tsgrep -rn --include="*.tsx" --include="*.ts" -E "ErrorBoundary|componentDidCatch|getDerivedStateFromError" src/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>asyncPromise# Find async functions
grep -rn --include="*.ts" --include="*.tsx" -E "async\s+function|async\s+\(" src/
# Find .then() without .catch()
grep -rn --include="*.ts" --include="*.tsx" -E "\.then\(" src/ | grep -v "\.catch\("// 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
}
}useQueryuseMutationisErrorerrorconst { data, isLoading, isError, error } = useQuery({
queryKey: ["assets"],
queryFn: () => fetchAssets(sdk),
});
if (isError) return <ErrorMessage error={error} />; // must be present| 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) |
grep -rn --include="*.tsx" -E "\.(map|filter|find)\(" src/ | grep -v "isLoading\|isPending\|skeleton\|Skeleton".map()localStorageJSON.parse# Find JSON.parse without validation
grep -rn --include="*.ts" --include="*.tsx" -E "JSON\.parse\(" src/
# Find localStorage reads
grep -rn --include="*.ts" --include="*.tsx" -E "localStorage\.(get|set)Item" src/
# Find useSearchParams usage
grep -rn --include="*.ts" --include="*.tsx" -E "useSearchParams|searchParams\.get" src/const parsed = MySchema.safeParse(raw);if (typeof value !== "string") return;const id = params.get("id") ?? defaultId;as MyType// 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 ?? "—";grep -rn --include="*.tsx" --include="*.ts" -E "\w+\[0\]\." src/useEffectgrep -rn --include="*.tsx" --include="*.ts" -B 2 -A 15 "useEffect" src/useEffect| Pattern | Required cleanup |
|---|---|
| |
| |
| CDF streaming / SSE | Close the stream |
| |
| Zustand / event emitter subscription | |
// 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]);limitexecuteargsexecute: async (args) => {
if (!args.assetId || typeof args.assetId !== "string") {
return { output: "Missing or invalid assetId", details: null };
}
// ... safe to proceed
}| 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 |