AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Js Debugging

skill-iwritec0de-app-dev-js-debugging · by iwritec0de

>-

No reviews yet
0 installs
38 views
0.0% view→install

Install

$ agentstack add skill-iwritec0de-app-dev-js-debugging

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-iwritec0de-app-dev-js-debugging)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
5mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Js Debugging? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

JavaScript/TypeScript Debugging Skill for Next.js Applications

Systematic debugging methodology for JavaScript, TypeScript, and Next.js applications. Follows four phases: root cause investigation, pattern analysis, hypothesis testing, and implementation.


Critical Rules

  1. No fixes without root cause. Never apply a fix unless you understand WHY the error occurs. Guessing wastes time and introduces new bugs.
  2. Read error messages completely. The full stack trace, including the "Caused by" chain and any nested errors. Do not stop at the first line.
  3. Check BOTH browser console AND server logs. Next.js runs code on both sides. A client error may originate server-side and vice versa.
  4. One change at a time. Make a single change, verify the result, then proceed. Batching changes makes it impossible to know what worked.
  5. Always create a failing test before fixing. If you cannot reproduce it in a test, you do not understand it well enough to fix it.

Phase 1: Root Cause Investigation (JS/TS-Specific)

Reading JavaScript Stack Traces

Stack traces are read top-to-bottom. The top frame is where the error was thrown; the bottom is where execution began.

TypeError: Cannot read properties of undefined (reading 'map')
    at UserList (src/components/UserList.tsx:14:23)    ← Error thrown here
    at renderWithHooks (react-dom.development.js:149)
    at mountIndeterminateComponent (react-dom.development.js:258)
    at beginWork (react-dom.development.js:312)

Key steps:

  • Look at the first frame that references YOUR code (not library code).
  • Check the file, line number, and column number. With source maps enabled, these map to your original TypeScript.
  • For async stack traces, look for async frames and the ... X more frames sections — these often contain the real origin.

Checking Browser DevTools Console

Open DevTools (Cmd+Option+I) and check the Console tab:

  • Red errors are runtime exceptions
  • Yellow warnings often indicate React-specific issues (key prop, deprecated APIs)
  • Check the "Preserve log" checkbox to retain errors across navigations
  • Filter by "Errors" to cut through noise

Checking Terminal / Server Logs

Next.js server errors appear in the terminal where next dev or next start is running:

  • Server component errors
  • API route errors
  • Middleware errors
  • Build-time errors

Look for [next] prefixed lines and the full error output including any cause property.

Strategic console.log at Component Boundaries

Place logs at the entry point of components to trace data flow:

export function UserList({ users }: { users: User[] }) {
  console.log('[UserList] render', { users, type: typeof users, isArray: Array.isArray(users) });

  // If users comes from a parent, trace it there too
  return (
    
      {users.map((user) => (
        {user.name}
      ))}
    
  );
}

Always log the type and shape of data, not just the value. undefined and null look the same when logged alone.

Using the debugger Statement

Drop a debugger statement to trigger a breakpoint when DevTools is open:

export async function getUser(id: string): Promise {
  const response = await fetch(`/api/users/${id}`);
  const data = await response.json();
  debugger; // Execution pauses here — inspect `data` in DevTools
  return data;
}

Remove debugger statements before committing. Consider a lint rule (no-debugger) to catch these.

TypeScript: Reading tsc Errors

Run tsc --noEmit to get the full list of type errors without producing output files:

pnpm tsc --noEmit

Read TypeScript errors from the bottom up. The last line of a TS error is usually the most specific:

src/components/UserList.tsx:14:5 - error TS2322: Type 'string' is not assignable to type 'number'.

14     count: userId,
       ~~~~~

  The expected type comes from property 'count' declared here:

    interface Props {
      count: number;
    }

The "expected type comes from" section tells you where the constraint originates.

Using node --inspect for Server-Side Debugging

Start the Next.js dev server with the Node.js inspector:

NODE_OPTIONS='--inspect' pnpm next dev

Then open chrome://inspect in Chrome and connect to the Node.js target. You can set breakpoints in server components, API routes, and middleware.


Phase 2: Common Bug Patterns (Next.js Focus)

Hydration Mismatch

Symptom: "Text content does not match server-rendered HTML" or "Hydration failed because the server-rendered HTML didn't match the client."

Cause: The HTML rendered on the server differs from what the client renders on first pass.

Diagnostic:

// BAD: This causes hydration mismatch because Date.now() differs server vs client
export function Timestamp() {
  return {Date.now()};
}

// GOOD: Use useEffect for client-only values
export function Timestamp() {
  const [time, setTime] = useState(null);

  useEffect(() => {
    setTime(Date.now());
  }, []);

  if (time === null) return Loading...;
  return {time};
}

Common causes and fixes:

  • Browser extensions injecting HTML — test in incognito mode
  • typeof window !== 'undefined' checks in render — move to useEffect
  • Dynamic imports that need ssr: false:
import dynamic from 'next/dynamic';

const MapComponent = dynamic(() => import('./Map'), { ssr: false });

"Cannot read properties of undefined"

Diagnostic approach: Trace the undefined value up the call chain.

// Error: Cannot read properties of undefined (reading 'email')
// at ProfileCard (src/components/ProfileCard.tsx:8:24)

export function ProfileCard({ user }: { user: User }) {
  // Add a guard and log to find WHERE user becomes undefined
  if (!user) {
    console.error('[ProfileCard] user is undefined — check parent component');
    return null;
  }

  return {user.email};
}

Then check the parent that passes user as a prop. The issue is almost always that the data has not loaded yet or an API returned an unexpected shape.

Unhandled Promise Rejection

Symptom: UnhandledPromiseRejection or Unhandled Runtime Error with an async operation.

Common causes:

// BAD: Missing await — the error is thrown but nobody catches it
function handleSubmit() {
  saveUser(formData); // Returns a Promise but we don't await it
}

// GOOD: Await and handle the error
async function handleSubmit() {
  try {
    await saveUser(formData);
  } catch (error) {
    console.error('Failed to save user:', error);
    setError('Failed to save. Please try again.');
  }
}
// BAD: async function passed to useEffect without wrapping
useEffect(async () => {
  const data = await fetchData();
  setData(data);
}, []);

// GOOD: Define async function inside useEffect
useEffect(() => {
  async function load() {
    try {
      const data = await fetchData();
      setData(data);
    } catch (error) {
      console.error('Failed to load data:', error);
    }
  }
  load();
}, []);

React Hook Rules Violation

Symptom: "Rendered more hooks than during the previous render" or "Hooks can only be called inside a function component."

Rule: Hooks must be called in the same order on every render. No conditionals, no loops, no early returns before hooks.

// BAD: Conditional hook
export function UserProfile({ userId }: { userId: string | null }) {
  if (!userId) return No user; // Early return BEFORE hook

  const [user, setUser] = useState(null); // Hook after conditional

  // ...
}

// GOOD: Hooks before any conditionals
export function UserProfile({ userId }: { userId: string | null }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    if (userId) {
      fetchUser(userId).then(setUser);
    }
  }, [userId]);

  if (!userId) return No user;

  return {user?.name};
}

Stale Closure

Symptom: A callback or effect reads an old value of state or props.

// BAD: count is captured at the time the interval is created
export function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      console.log(count); // Always logs 0
      setCount(count + 1); // Always sets to 1
    }, 1000);
    return () => clearInterval(id);
  }, []); // Empty deps — closure captures initial count

  return {count};
}

// GOOD: Use functional updater to avoid stale closure
export function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setCount((prev) => prev + 1); // Always reads latest value
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return {count};
}

For non-state values, use useRef to hold a mutable reference:

const callbackRef = useRef(onSave);
callbackRef.current = onSave; // Update on every render

useEffect(() => {
  // callbackRef.current always points to the latest onSave
  callbackRef.current(data);
}, [data]);

Infinite Re-render

Symptom: "Maximum update depth exceeded" or the browser freezes.

// BAD: setState called during render
export function UserList({ users }: { users: User[] }) {
  const [sorted, setSorted] = useState([]);
  setSorted(users.sort()); // Called on every render → infinite loop
  return {sorted.map((u) => {u.name})};
}

// GOOD: Derive state with useMemo instead of setState
export function UserList({ users }: { users: User[] }) {
  const sorted = useMemo(() => [...users].sort((a, b) => a.name.localeCompare(b.name)), [users]);
  return {sorted.map((u) => {u.name})};
}
// BAD: Object literal in deps causes re-run every render
useEffect(() => {
  fetchData({ page: 1, limit: 10 });
}, [{ page: 1, limit: 10 }]); // New object reference every render

// GOOD: Use primitive values or useMemo for deps
const page = 1;
const limit = 10;
useEffect(() => {
  fetchData({ page, limit });
}, [page, limit]);

Module Not Found

Diagnostic steps:

  1. Check path aliases in tsconfig.json:
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}
  1. Check if the import is server-only being used in a client component:
// This will fail in a client component
import { db } from '@/lib/db'; // Server-only module

// Fix: Move the data fetching to a server component or API route
  1. Check barrel exports — a barrel file re-exporting a server module can break client bundles:
// src/lib/index.ts (barrel)
export { db } from './db';       // Server-only
export { cn } from './utils';    // Works everywhere

// Importing `cn` from the barrel pulls in `db` too → breaks client bundle
// Fix: Import directly from the specific file
import { cn } from '@/lib/utils';

API Route Errors

// Common issues in Next.js App Router API routes

// BAD: Not parsing the request body
export async function POST(request: Request) {
  const body = request.body; // This is a ReadableStream, not parsed JSON
  // ...
}

// GOOD: Parse JSON body correctly
export async function POST(request: Request) {
  const body = await request.json();
  // ...
}

// BAD: Returning plain object (App Router requires Response)
export async function GET() {
  return { users: [] }; // Does not work
}

// GOOD: Return a Response or use NextResponse
import { NextResponse } from 'next/server';

export async function GET() {
  return NextResponse.json({ users: [] });
}

Check auth middleware is not silently blocking requests. Add logging in middleware:

// src/middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  console.log('[middleware]', request.method, request.nextUrl.pathname);
  // ...
}

Build vs Runtime Errors

Some errors only appear in production builds. Always test with:

pnpm next build && pnpm next start

Common causes of build-only errors:

  • Environment variables missing in production (not prefixed with NEXT_PUBLIC_ for client access)
  • Dynamic imports with expressions that cannot be statically analyzed
  • Edge runtime incompatibility (Node.js APIs not available):
// This fails on Edge runtime
import { readFileSync } from 'fs';

// Check if you're accidentally using Edge runtime:
// export const runtime = 'edge'; ← Remove if you need Node.js APIs

Type Errors

as casting hiding real issues:

// BAD: Casting masks the real problem
const user = apiResponse as User; // What if apiResponse is actually an error?

// GOOD: Validate the shape at runtime
import { z } from 'zod';

const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
});

const result = UserSchema.safeParse(apiResponse);
if (!result.success) {
  console.error('Invalid user data:', result.error.flatten());
  throw new Error('Invalid user data from API');
}
const user = result.data; // Properly typed and validated

strictNullChecks violations:

// Error: Object is possibly 'undefined'
const users = await getUsers();
const firstEmail = users[0].email; // users[0] could be undefined

// Fix: Guard against undefined
const firstEmail = users[0]?.email ?? 'No email';
// Or with explicit check
if (users.length === 0) {
  throw new Error('No users found');
}
const firstEmail = users[0].email; // Safe after length check

Generic inference failures:

// TypeScript cannot infer the generic type
const result = useQuery({ queryKey: ['users'], queryFn: getUsers });
// result.data is unknown

// Fix: Provide the generic explicitly
const result = useQuery({ queryKey: ['users'], queryFn: getUsers });
// result.data is User[] | undefined

Phase 3: Diagnostic Techniques

React DevTools

Install the React DevTools browser extension and use these features:

  • Components tab: Inspect the component tree, view props and state for any component, and identify which component owns a piece of state.
  • Profiler tab: Record a session, then review which components re-rendered and why.
  • Highlight updates: Enable "Highlight updates when components render" in DevTools settings to visually see unnecessary re-renders.

Network Tab

For API debugging, use the browser Network tab:

  • Filter by Fetch/XHR to see API calls
  • Check the Status column for non-200 responses
  • Click a request to see the full Request Headers, Request Body, and Response Body
  • Look for CORS errors: Access-Control-Allow-Origin header missing
  • Check Timing tab to identify slow requests

React.Profiler for Performance Debugging

Wrap suspect components to measure render times:

import { Profiler } from 'react';

function onRender(
  id: string,
  phase: 'mount' | 'update',
  actualDuration: number,
) {
  if (actualDuration > 16) {
    console.warn(`[Profiler] ${id} ${phase} took ${actualDuration.toFixed(2)}ms`);
  }
}

export function App() {
  return (
    
      
    
  );
}

why-did-you-render for Unnecessary Re-renders

Install and configure to detect avoidable re-renders:

// src/wdyr.ts — import this BEFORE React in your entry point
import React from 'react';

if (process.env.NODE_ENV === 'development') {
  const { default: whyDidYouRender } = await import(
    '@welldone-software/why-did-you-render'
  );
  whyDidYouRender(React, {
    trackAllPureComponents: true,
  });
}

Then tag specific components:

function UserList({ users }: { users: User[] }) {
  // ...
}
UserList.whyDidYouRender = true;

Next.js Specific Diagnostics

Inspect the build output:

ls -la .next/
# Check .next/server/ for server component output
# Check .next/static/ for client bundles

**Get env

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.