.agents/skills/astro-developer/debugging.md
Practical debugging strategies for common issues in Astro development.
What's failing? → Debug with:
| Symptom | First Check | Debug Command | Section |
|---|---|---|---|
| Build fails | Astro build logs | DEBUG=astro:* pnpm -C packages/astro build | Build Issues |
| Dev server crash | Core logs | DEBUG=astro:* astro dev | Core Issues |
| HMR not working | Browser network | agent-browser (not curl) | HMR Issues |
| SSR fails | Runtime context | DEBUG=astro:* astro dev | SSR Issues |
| Content missing | Data store | cat .astro/data-store.json | Content |
| Tests failing | Fixture setup | Check outDir uniqueness | Tests |
Most issues are in Astro's code, not Vite. Use Astro-specific debugging first.
# Debug everything in Astro
DEBUG=astro:* astro dev
DEBUG=astro:* astro build
# Debug specific subsystems
DEBUG=astro:build astro build # Build process
DEBUG=astro:content astro dev # Content collections
DEBUG=astro:server astro dev # Dev server
DEBUG=astro:render astro dev # Page rendering
DEBUG=astro:config astro dev # Configuration
# Combine multiple namespaces
DEBUG=astro:build,astro:config astro build
DEBUG=astro:render,astro:server astro dev
Fastest approach: Add console.log directly in source files.
// Pattern: Add logs with context prefix
console.log('[CONTEXT] Message:', data);
// Example
console.log('[BUILD] Processing routes:', routes.length);
console.log('[RENDER] Component:', component.name);
console.log('[CONTENT] Collections:', collections);
Workflow:
pnpm -C packages/astro build to rebuildUse Astro's debug logger (optional):
import { debug } from '../logger/core.js';
const logger = debug('astro:feature-name');
logger('Operation starting', { data });
# Start with debugger
node --inspect node_modules/.bin/astro dev
node --inspect node_modules/.bin/astro build
# Connect with Chrome DevTools
# Open chrome://inspect in Chrome
# Click "inspect" on the Node.js process
Set breakpoints in Chrome DevTools, navigate to source files, click line numbers.
Where to add logs based on issue type:
| Issue Type | File Location | Context |
|---|---|---|
| Build fails | packages/astro/src/core/build/index.ts | Core |
| Routes not found | packages/astro/src/core/routing/manifest/create.ts | Core |
| Content missing | packages/astro/src/content/content-layer.ts | Core |
| Rendering errors | packages/astro/src/core/render/core.ts | Runtime |
| Config issues | packages/astro/src/core/config/config.ts | Core |
| Dev server issues | packages/astro/src/core/dev/dev.ts | Core |
| Component compile | packages/astro/src/vite-plugin-astro/index.ts | Vite |
| Virtual modules | packages/astro/src/vite-plugin-*/ | Vite |
| Middleware issues | packages/astro/src/core/middleware/ | Core |
| Adapter issues | Check specific adapter in packages/integrations/ | Integration |
Core code runs in Node.js context: packages/astro/src/core/
This is where most Astro bugs live.
Entry point: packages/astro/src/core/build/index.ts
Flow:
build() → Main entrystaticBuild() or viteBuild() → Build strategydist/Build plugin order (packages/astro/src/core/build/plugins/README.md):
Add tracing logs to understand flow:
// In build/index.ts
console.log('[1] build() entry');
console.log('[2] Settings created');
console.log('[3] Build complete');
Astro Vite plugins: packages/astro/src/vite-plugin-*/
vite-plugin-astro → .astro file compilationvite-plugin-astro-server → Dev server integrationvite-plugin-env → Environment variablesvite-plugin-html → HTML injectionBuild plugins: packages/astro/src/core/build/plugins/
plugin-middleware.ts → Middleware emissionplugin-renderers.ts → Renderer collectionplugin-pages.ts → Page virtual modulesplugin-ssr.ts → SSR entry pointsplugin-manifest.ts → Manifest generationSSR issues span multiple contexts. Identify context first.
Determine which context the issue occurs in:
astro dev? → Dev/render contextastro build? → Build contextastro preview? → Runtime/adapter contextSee architecture.md for pipeline details.
Dev SSR:
packages/astro/src/core/render/DEBUG=astro:render,astro:server astro devBuild SSR:
packages/astro/src/core/build/DEBUG=astro:build astro builddist/ structure, hashed chunks in dist/server/chunks/Runtime SSR:
packages/astro/src/core/app/astro preview# Inspect dist/ structure
ls -laR dist/
# SSR build structure (varies by adapter pattern):
# dist/client/ → Client assets (hashed)
# dist/server/chunks/ → All server code (hashed files)
# dist/server/virtual_astro_middleware.mjs → Middleware
# dist/server/[entrypoint] → Entry point (filename depends on adapter)
# Legacy adapters: Use entry.mjs
# Self adapters: Adapter decides filename (e.g., custom.mjs, _render.mjs)
Adapter patterns:
adapter.entrypointResolution = 'explicit'): Always uses entry.mjsadapter.entrypointResolution = 'self'): Adapter controls entrypoint filenameFind entrypoint:
# List server files (entrypoint is typically at top level)
ls dist/server/*.mjs
# Check entrypoint content (always a re-export to chunks)
cat dist/server/entry.mjs # or whatever the adapter named it
Find actual code:
# All server code is in hashed chunks
ls dist/server/chunks/
# Search for specific code in chunks
grep -r "function.*render" dist/server/chunks/
Virtual modules use virtual:astro:* prefix.
virtual:astro:manifest → Manifest datavirtual:astro:routes → Route definitionsvirtual:astro:middleware → Middleware modulevirtual:astro:renderers → Framework renderersAdd logging to Vite plugin hooks:
// In Vite plugin
{
resolveId: {
handler(id) {
if (id.includes('virtual:astro')) {
console.log('[VIRTUAL] Resolving:', id);
}
// ...
}
},
load: {
handler(id) {
if (id.includes('\0virtual:astro')) {
console.log('[VIRTUAL] Loading:', id);
const code = generateCode();
console.log('[VIRTUAL] Generated code:', code);
return { code };
}
}
}
}
# See loaded virtual modules
DEBUG=astro:* astro dev 2>&1 | grep "virtual:astro"
Content layer issues often relate to data store or type generation.
# View entire data store
cat .astro/data-store.json | jq
# Check specific collection
cat .astro/data-store.json | jq '.collections["blog"]'
# Count entries per collection
cat .astro/data-store.json | jq '.collections | to_entries | map({key: .key, count: .value.entries | length})'
Location: packages/astro/src/content/content-layer.ts
Enable debugging:
DEBUG=astro:content astro dev
DEBUG=astro:content astro build
Location: .astro/types.d.ts
# View generated types
cat .astro/types.d.ts | grep -A 20 "declare module 'astro:content'"
Add logging in your loader implementation:
export function myLoader() {
return {
name: 'my-loader',
async load({ store, logger }) {
logger.info('Loading data...');
const data = await fetchData();
logger.info(`Loaded ${data.length} entries`);
for (const entry of data) {
console.log('[LOADER] Setting:', entry.id);
store.set({ id: entry.id, data: entry });
}
},
};
}
HMR testing requires a browser. Do not use curl for HMR issues.
# Start dev server in background
pnpm exec bgproc start -n devserver --wait-for-port 10 -- pnpm -C examples/minimal dev
# Open browser
agent-browser open http://localhost:4321
# Get snapshot
agent-browser snapshot -i
# Make changes to source files
# Verify HMR updates the page
# View logs
pnpm exec bgproc logs -n devserver
# Cleanup
pnpm exec bgproc stop -n devserver
Vite maintains HMR boundaries. If HMR isn't working, check module boundaries.
DEBUG=vite:hmr astro dev
Look for:
| Issue | Cause | Fix |
|---|---|---|
| Full page reload | No HMR boundary | Add HMR accept |
| Styles not updating | CSS module cache | Check Vite CSS handling |
| Component not updating | Module not in graph | Check import chain |
# Build with full output
astro build
# Inspect dist/ structure
ls -laR dist/
# SSR build structure:
# dist/client/ → Client assets (hashed)
# dist/server/[entrypoint] → Entry shim (filename varies by adapter)
# dist/server/chunks/ → All server code (hashed files)
# Find entrypoint (adapter-dependent filename)
ls dist/server/*.mjs
# Check entry point (always a re-export to chunks)
cat dist/server/entry.mjs # or whatever filename adapter uses
# All actual code is in hashed chunks
ls dist/server/chunks/
Order matters. Plugins execute sequentially.
# Check plugin execution
DEBUG=vite:* astro build 2>&1 | grep "plugin-"
Check:
dist/client/ → Client assetsdist/server/ → SSR code# Find asset references
find dist/ -name "*.html" -exec grep -l "asset-file.jpg" {} \;
See testing.md for comprehensive test debugging.
Quick checks:
.astro/ and dist/ in fixture--parallel is causing issuesCause: Using Node.js API in runtime/ code
Fix: Move code to core/ or use @astrojs/internal-helpers
See: constraints.md
Cause: Virtual module not registered or plugin not loaded
Fix:
resolveId and load hooks use filter/handler patternvirtual:astro:*Cause: Shared outDir between tests causing cache pollution
Fix: Set unique outDir for each test fixture
See: testing.md
Cause: Previous dev server still running
Fix:
# List running processes
pnpm exec bgproc list
# Stop specific server
pnpm exec bgproc stop -n devserver
# Kill all node processes (nuclear option)
killall node
Cause: Module boundaries, full reload triggered, or browser cache
Fix:
agent-browser (not curl)DEBUG=vite:hmrBefore asking for help or filing an issue:
examples/