| name | debugging |
| description | Debug issues in the Second Brain Nuxt 4 + @nuxt/content v3 project. Use for any bug, test failure, or unexpected behavior. |
Second Brain Debugging
Overview
This skill adapts systematic debugging for the Second Brain stack:
- Nuxt 4 with Vue 3 Composition API
- @nuxt/content v3 with SQLite and minimark format
- @nuxt/ui v4 components
- D3.js for knowledge graph visualization
Core principle: ALWAYS trace data flow before attempting fixes.
Stack-Specific Gotchas
Before debugging, internalize these common pitfalls:
| Issue | Symptom | Cause |
|---|---|---|
| Empty graph edges | Nodes show, no connections | Missing .select('body') in query |
| Links not extracted | Backlinks/graph empty | Minimark parsed as object, not array |
| 404 on content page | Page not found | Slug mismatch (/slug vs slug) |
| Stale backlinks | Old connections shown | useAsyncData cache not invalidated |
| Silent API failure | Empty response, no error | try-catch returns {} or [] |
| Wrong mentions | Unrelated content matched | Title regex too broad |
| Graph crashes on filter | D3 error after filtering | Edge source/target type mismatch |
The Four Phases
Phase 1: Identify the Layer
Data flows through these layers:
Content File (Markdown)
↓ Parsed by Nuxt Content
Minimark AST (body.value array)
↓ Queried via queryCollection
API Endpoint (server/api/*.ts)
↓ Returned as JSON
Vue Composable (useBacklinks, useMentions)
↓ Rendered in component
User Interface
First question: Which layer is broken?
- Content layer - Check frontmatter, markdown syntax
- AST layer - Check minimark structure:
[tag, props, ...children] - Query layer - Check
.select('body'), path matching - API layer - Check response structure, error handling
- Client layer - Check composable logic, reactivity
Quick diagnostic:
# Check if content is queryable
pnpm nuxi dev
# Visit http://localhost:3000/api/graph
# Empty edges? → Layer 2-3 issue
# Empty nodes? → Layer 1 issue
# Correct data but UI wrong? → Layer 4-5 issue
Phase 2: Trace Data Flow
For content/link issues:
Check raw content file:
- Frontmatter has
titleandtype? - Wiki-links use correct format
[[slug]]? - File in
/content/directory?
- Frontmatter has
Check minimark extraction:
// In server/api/graph.get.ts, temporarily add: console.log('Body structure:', JSON.stringify(item.body, null, 2)) // Verify: { type: 'minimark', value: [[...arrays...]] } // NOT: { type: 'minimark', value: [{...objects...}] }Check query selection:
// Must explicitly select body: queryCollection(event, 'content') .select('path', 'stem', 'title', 'type', 'body') // ← body required! .all()Check API response:
curl http://localhost:3000/api/graph | jq '.edges | length' curl http://localhost:3000/api/backlinks | jq 'keys | length'
For graph visualization issues:
- Check nodes exist before filtering
- Check edge source/target types (string vs object after D3)
- Check filter state in URL query params
Phase 3: Hypothesis Testing
Form ONE hypothesis:
- "The body isn't being selected because..."
- "Links aren't extracted because minimark format is..."
- "The cache is stale because..."
Test minimally:
# Run relevant tests first
pnpm test:unit -- minimark # For link extraction
pnpm test:run -- graph # For API issues
Add targeted logging:
// In extractLinksFromBody (server/utils/minimark.ts)
console.log('Body type:', body?.type)
console.log('Body value is array?', Array.isArray(body?.value))
console.log('First node:', JSON.stringify(body?.value?.[0]))
Phase 4: Fix and Verify
Create failing test first:
// tests/unit/utils/minimark.test.ts it('extracts links from nested minimark', () => { const body = { type: 'minimark', value: [['a', { href: '/target' }, 'Link text']] } expect(extractLinksFromBody(body)).toContain('target') })Implement fix in ONE place
Verify with full test suite:
pnpm test:run pnpm lint:fix && pnpm typecheckIf fix doesn't work after 3 attempts:
- Question the architecture
- Is the data model fundamentally sound?
- Should slug normalization be centralized?
Quick Debugging Commands
# Run all tests
pnpm test:run
# Run specific test file
pnpm test:unit -- minimark
pnpm test:run -- graph.nuxt
# Type check
pnpm typecheck
# Start dev server for manual testing
pnpm dev
# Check API responses
curl -s http://localhost:3000/api/graph | jq '.nodes | length'
curl -s http://localhost:3000/api/graph | jq '.edges | length'
curl -s http://localhost:3000/api/backlinks | jq 'keys'
curl -s "http://localhost:3000/api/mentions?slug=test&title=Test" | jq
Key Files by Subsystem
Content Pipeline
/content/*.md- Raw content files/content.config.ts- Collection schema (title, type required)/modules/wikilinks.ts- [[link]] to anchor transformation
Link Extraction
/server/utils/minimark.ts- Extract links from AST- Body format:
{ type: 'minimark', value: [...arrays...] } - Link format:
['a', { href: '/slug' }, 'text']
API Endpoints
/server/api/graph.get.ts- Nodes and edges for D3/server/api/backlinks.get.ts- Reverse link index/server/api/mentions.get.ts- Unlinked mentions search
Client Composables
/app/composables/useBacklinks.ts- Fetch backlinks/app/composables/useMentions.ts- Fetch mentions/app/composables/useGraphFilters.ts- Graph filter state
Pages
/app/pages/[...slug].vue- Content page/app/pages/graph.vue- Knowledge graph
Common Fixes
"Graph shows nodes but no edges"
// Fix: Add .select('body') to query
queryCollection(event, 'content')
.select('path', 'stem', 'title', 'type', 'tags', 'body') // ← ADD body
.all()
"Links not being extracted"
// Check minimark format - must be ARRAY based
// Wrong: { tag: 'a', props: { href: '/' }, children: [] }
// Right: ['a', { href: '/' }, 'text']
// Fix extractLinksFromMinimark to handle arrays:
function extractLinksFromMinimark(node: unknown): string[] {
if (!Array.isArray(node)) return []
const [tag, props, ...children] = node
// ...
}
"Stale backlinks after content edit"
// Force refresh with key
const { data: backlinks, refresh } = await useAsyncData(
`backlinks-${Date.now()}`, // or add content hash
() => $fetch('/api/backlinks')
)
"Content page 404"
// Check slug normalization
// Route uses: /atomic-habits (with slash)
// queryCollection expects path: '/atomic-habits' (with slash)
// Backlinks use slug: 'atomic-habits' (no slash)
// Ensure consistent normalization:
const slug = route.path.startsWith('/') ? route.path : `/${route.path}`
Red Flags - STOP
If you catch yourself:
- Guessing which layer the bug is in
- Adding fixes to multiple files at once
- Skipping the test before fixing
- Not checking minimark format
- Assuming body is included by default
STOP. Return to Phase 1.