Debug Local logo

Debug Local

OrganizationPopular
lightdash
debug-local

Debug the Lightdash app using PM2 logs, Maple traces, and browser automation. Use when investigating issues, tracking down bugs, understanding request flow, or correlating frontend actions with backend behavior.

Overview

Publisherlightdash
Repositorylightdash
Skill namedebug-local
Stars
6.2K
Forks
790
Bundled files
Instructions only
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by lightdash on GitHub. Read the source before you install it.

Installation

Install the Debug Local AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/lightdash/lightdash/tree/main/.claude/skills/debug-local
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/lightdash/lightdash.git /tmp/lightdash
mkdir -p .claude/skills
cp -r /tmp/lightdash/.claude/skills/debug-local .claude/skills/debug-local
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Debug Local in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Debug Local on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Debug Local is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Debugging Lightdash

Iron Law

NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST.

Do not edit application code until you have a confirmed root cause hypothesis backed by evidence (logs, traces, reproduction). Fixing symptoms creates whack-a-mole debugging.


Phase 1: Gather Evidence

Before forming any hypothesis, collect facts.

1. Collect symptoms

Read error messages, stack traces, and reproduction steps. If the user hasn't provided enough context, ask ONE clarifying question.

2. Check recent changes

bash
git log --oneline -20 -- <affected-files>

Was this working before? A regression means the root cause is in the diff.

3. Check logs and traces

bash
pnpm exec pm2 logs lightdash-api --lines 50 --nostream

Then query Maple (local mode ingests OTLP traces from the API and scheduler):

bash
maple services --format table       # per-service throughput and error rate
maple errors --since 5m             # errors grouped by fingerprint
maple traces --since 5m --errors    # recent traces containing errors
maple trace <trace-id>              # full span tree + correlated logs
maple slow-traces --since 15m       # slowest traces

The CLI defaults to port 4318, so point it at this instance first: export MAPLE_LOCAL_URL="http://127.0.0.1:${MAPLE_PORT:-4320}". See Trace Lookup for the full filter set.

4. Reproduce

Can you trigger the bug deterministically? Use browser automation or curl to reproduce. If you can't reproduce, gather more evidence before proceeding.

5. Read the code

Trace the code path from the symptom back to potential causes. Use Grep to find all references, Read to understand the logic.

Output: State your root cause hypothesis — a specific, testable claim about what is wrong and why.


Phase 2: Pattern Matching

Check if the bug matches a known Lightdash pattern:

PatternSignatureWhere to look
Permission error403 ForbiddenCASL abilities in projectMemberAbility.ts, organizationMemberAbility.ts
Slow query / N+1Response >1s, many db spansmaple trace <id> span tree — count db.* spans, check for loops
Stale explore cacheShows old columns/metricscached_explores table, CompileService
Migration issueColumn not found, schema mismatchpackages/backend/src/database/migrations/, check rollback
Scheduler job failureJob not running, stuckPM2 scheduler logs, graphile_worker.jobs table
Frontend stale dataUI shows old data after mutationTanStack Query cache invalidation, check queryClient.invalidateQueries
Race conditionIntermittent, timing-dependentConcurrent access to shared state, missing await
Cross-service file issueFile not found in worker/headlessMust use S3 via FileStorageClient, not local filesystem
Config driftWorks locally, fails in CI/stagingEnv vars, .env.development.local vs container env
Null propagationTypeError, cannot read propertyMissing guards on optional values, Knex .first() returning undefined

Also check:

  • git log for prior fixes in the same area — recurring bugs in the same files are an architectural smell
  • Whether the issue is in common, backend, or frontend — misattributing the layer wastes time

Phase 3: Hypothesis Testing

Before writing ANY fix, verify your hypothesis.

  1. Confirm: Add a temporary log, assertion, or use Maple/PM2 to check the suspected root cause. Reproduce. Does evidence match?

  2. If wrong: Return to Phase 1. Gather more evidence. Do not guess.

  3. 3-strike rule: If 3 hypotheses fail, STOP. Ask the user:

    • Continue with a new hypothesis (describe it)
    • Escalate for human review
    • Add instrumentation and wait to catch it next time

Phase 4: Fix

Once root cause is confirmed:

  1. Fix the root cause, not the symptom. Smallest change that eliminates the actual problem.

  2. Minimal diff. Fewest files touched, fewest lines changed. Do not refactor adjacent code.

  3. Write a regression test that fails without the fix and passes with it.

  4. Run the relevant test suite. Paste output.

  5. Blast radius check: If fix touches >5 files, pause and confirm with the user before proceeding.


Phase 5: Verify & Report

Reproduce the original bug scenario and confirm it's fixed. This is not optional.

Output a structured debug report:

DEBUG REPORT
════════════════════════════════════════
Symptom:         [what the user observed]
Root cause:      [what was actually wrong]
Fix:             [what was changed, with file:line references]
Evidence:        [test output, trace showing fix works]
Regression test: [file:line of the new test]
Status:          DONE | DONE_WITH_CONCERNS | BLOCKED
════════════════════════════════════════

Status definitions:

  • DONE — root cause found, fix applied, regression test written, tests pass
  • DONE_WITH_CONCERNS — fixed but cannot fully verify (e.g., intermittent bug, needs staging)
  • BLOCKED — root cause unclear after investigation, escalated to user

Prerequisites

Ensure the development environment is running:

bash
/docker-dev              # Start Docker services (postgres, minio, etc.)
pnpm pm2:start           # Start all PM2 processes including Maple
pnpm pm2:status          # Verify all processes are online

Services:

Tools Reference

Server Logs (PM2)

bash
pnpm pm2:logs              # Stream all logs (Ctrl+C to exit)
pnpm pm2:logs:api          # API server logs only
pnpm pm2:logs:scheduler    # Background job scheduler logs
pnpm pm2:logs:frontend     # Vite dev server logs
pnpm pm2:logs:maple        # Maple tracing server logs

For non-blocking log viewing (last N lines):

bash
pnpm exec pm2 logs lightdash-api --lines 20 --nostream
Log format

Lightdash logs include a 32-character trace ID for correlation:

[700aa55e784a437aa97de9b8c5c3ed3a] GET /api/v1/health 200 - 37 ms
[4d40816e5a7d433e88f99008de5f4be5] GET /api/v1/user/login-options 200 - 2 ms

Pass the full 32-character ID to maple trace to pull up the request.

Trace Lookup (Maple CLI)

Maple local mode stores traces, logs, and metrics in an embedded ClickHouse and exposes them through the maple CLI. Every command takes --since <30m|1h|24h|7d> for the window. Output is JSON — pipe through jq to narrow it. --format table only applies to flat row sets (services, query); the other commands say so and fall back to JSON.

Set the target once per shell — the CLI defaults to port 4318, and each worktree runs its own server on its slot port:

bash
export MAPLE_LOCAL_URL="http://127.0.0.1:${MAPLE_PORT:-4320}"
List services
bash
maple services --format table

Throughput, error rate, and P95 latency per service. The API reports as $LD_INSTANCE_ID (lightdash by default) and the worker as $LD_INSTANCE_ID-scheduler.

List recent traces
bash
maple traces --since 5m | jq '.traces[] | {traceId, spanName, durationMs}'
maple traces --since 1h --errors --min-duration-ms 500
maple slow-traces --since 15m

Filters: --span-name <substr>, --http-method, --errors, --min-duration-ms, --max-duration-ms, --service, --limit.

Get trace details
bash
maple trace 700aa55e784a437aa97de9b8c5c3ed3a

Returns the full span tree plus the logs correlated to that trace.

Search for errors
bash
maple errors --since 30m        # grouped by fingerprint
maple error <fingerprint-hash>  # sample traces + timeseries
Search logs
bash
maple logs --since 5m --severity ERROR
maple logs --trace-id 700aa55e784a437aa97de9b8c5c3ed3a
maple logs -q "compile" --since 1h
Raw SQL escape hatch
bash
maple query "SELECT ServiceName, count() FROM traces GROUP BY ServiceName"

Browser Debugging (Chrome DevTools MCP)

Use the Chrome DevTools MCP tools for browser automation:

Opening pages
mcp__chrome-devtools__new_page with url: "http://localhost:3000/login"
mcp__chrome-devtools__navigate_page with url: "http://localhost:3000", type: "url"
Taking snapshots
mcp__chrome-devtools__take_snapshot

Returns a text snapshot of the page based on the accessibility tree with unique uid identifiers for each element.

Interacting with elements
mcp__chrome-devtools__click with uid: "1_5"
mcp__chrome-devtools__fill with uid: "1_4", value: "test@example.com"
mcp__chrome-devtools__hover with uid: "1_3"
Screenshots
mcp__chrome-devtools__take_screenshot
mcp__chrome-devtools__take_screenshot with fullPage: true
mcp__chrome-devtools__take_screenshot with filePath: "/tmp/debug.png"
Console and network
mcp__chrome-devtools__list_console_messages
mcp__chrome-devtools__list_network_requests
mcp__chrome-devtools__get_network_request with reqid: 123
Page management
mcp__chrome-devtools__list_pages
mcp__chrome-devtools__select_page with pageId: 1
mcp__chrome-devtools__close_page with pageId: 2

Database Inspection

bash
# Check table schema
psql -c "\d <table_name>"

# Query data directly
psql -c "SELECT * FROM <table> WHERE <condition> LIMIT 5;"

# Check scheduler jobs
psql -c "SELECT id, task_identifier, attempts, last_error FROM graphile_worker.jobs WHERE last_error IS NOT NULL LIMIT 10;"

Trace Attributes (Wide Events)

Traces contain contextual attributes beyond timing:

AttributeExampleDescription
http.route/api/v1/projects/:projectUuidRoute pattern
http.status_code200Response status
http.methodGETHTTP method
sentry.opdb, http.serverOperation type
db.systempostgresqlDatabase type

Common Debugging Scenarios

SymptomWhat to check
401 UnauthorizedTrace auth middleware, check session/JWT spans
403 ForbiddenCheck user ability/permissions in trace attributes, review CASL abilities
404 Not FoundVerify route exists, check resource lookup spans
400 Bad RequestLook for validation errors in trace/error logs
Slow responseCheck span breakdown for slow db.* or external calls, count db spans for N+1
Empty resultsVerify query parameters, check db query spans
500 Server ErrorUse search_errors for stack trace and context
UI not updatingCheck TanStack Query devtools, verify cache invalidation after mutations
Scheduler not runningCheck pnpm pm2:logs:scheduler, query graphile_worker.jobs table

Quick Commands Reference

ActionCommand
View all logspnpm pm2:logs
View API logs (non-blocking)pnpm exec pm2 logs lightdash-api --lines 20 --nostream
Check process statuspnpm pm2:status
Restart APIpnpm pm2:restart:api
Recent tracesmaple traces --since 5m
Trace detailsmaple trace <full-trace-id>
Recent errorsmaple errors --since 5m
Browser snapshotmcp__chrome-devtools__take_snapshot
Open Maple trace UIhttp://localhost:4320

Cross-Agent Validation

Use another AI agent to validate your findings, challenge your conclusions, and provide independent evidence. This is not just for when you're stuck — actively seek validation throughout the debugging process to ensure your analysis is sound.

Detect your environment

bash
which claude 2>/dev/null && echo "HAS_CLAUDE=true" || echo "HAS_CLAUDE=false"
which codex 2>/dev/null && echo "HAS_CODEX=true" || echo "HAS_CODEX=false"

If you are Claude → ask Codex

bash
codex exec "Given this context from a Lightdash debugging session:

<context>
[paste relevant logs, traces, error messages, or code snippets]
</context>

<question>
[your specific question — e.g., 'Does this trace confirm that the query cache is being bypassed?' or 'Given this stack trace, is my conclusion that the middleware short-circuits before auth correct?']
</question>"

If you are Codex → ask Claude

bash
claude -p "Given this context from a Lightdash debugging session:

<context>
[paste relevant logs, traces, error messages, or code snippets]
</context>

<question>
[your specific question]
</question>"

When to consult

  • Validate a hypothesis: Before concluding root cause, ask the other agent if the evidence supports your theory or if there's an alternative explanation
  • Verify your interpretation of data: When reading traces, logs, or query output — confirm you're reading it correctly
  • Challenge your fix: Before suggesting a code change, ask whether the fix actually addresses the root cause or just masks a symptom
  • Cross-check complex logic: When the issue involves multiple systems (frontend + API + database + permissions), get an independent read on the interaction
  • Justify a conclusion: If you're about to tell the user "X is the cause", make sure another perspective agrees — or surface the disagreement

Guidelines

  • Set a 5-minute timeout: Codex can take a while to respond. When running codex exec via the Bash tool, set the Bash timeout to 300000ms (5 minutes) to avoid premature termination. Do NOT pass --timeout to codex exec itself — it doesn't support that flag.
  • Be specific: Include the actual error, trace output, or code snippet — not just a vague description
  • Ask for validation, not just answers: "Does this evidence support my conclusion that X?" is better than "What's wrong?"
  • Include your current hypothesis: Let the other agent confirm or challenge it, rather than starting from scratch
  • Include what you've already ruled out: This avoids duplicate investigation and focuses the consultation
  • Always report back to the user: When you consult another agent, tell the user you did so. Summarize what you asked, what the other agent found, and whether you agree with their assessment. If there's a disagreement, present both perspectives and the supporting evidence for each. The user should never be unaware that another agent was consulted.

Test User Credentials

For testing authenticated flows:

API Access (Personal Access Token)

Prefer this method over using fetch() via Chrome DevTools MCP for API calls. curl with the PAT is faster, more reliable, and doesn't require a browser session or page context. Reserve browser MCP tools for UI interaction and visual debugging only.

A dev PAT is auto-provisioned by the seed data. Use it for direct API calls without browser login:

bash
# Source the env file to get LDPAT and LIGHTDASH_API_URL
source .env.development.local

# Example: list projects
curl -s -H "Authorization: ApiKey $LDPAT" "$LIGHTDASH_API_URL/api/v1/org/projects" | jq

# Example: get user info
curl -s -H "Authorization: ApiKey $LDPAT" "$LIGHTDASH_API_URL/api/v1/user" | jq

The token (ldpat_deadbeefdeadbeefdeadbeefdeadbeef) is defined in SEED_PAT from @lightdash/common and inserted during database seeding. It belongs to the admin user (demo@lightdash.com) and never expires.

Frequently asked questions

What does the Debug Local AI skill do?

Debug the Lightdash app using PM2 logs, Maple traces, and browser automation. Use when investigating issues, tracking down bugs, understanding request flow, or correlating frontend actions with backend behavior.

Why use Debug Local on TypingMind?

Because you install it once and use it with any model. Debug Local is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Debug Local in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/lightdash/lightdash/tree/main/.claude/skills/debug-local. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Debug Local?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Debug Local?

As many as you like. As long as a model supports skills, you can use Debug Local with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Debug Local AI skill free?

It is published on GitHub by lightdash. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇