skills/debug-task/references/environment-debug.md
This reference covers the debug environment variables, log levels, and inspection tools available for deep debugging of moon tasks.
moon provides several environment variables that reveal internal state during task execution. Set
them before running moon run:
| Variable | What it reveals |
|---|---|
MOON_DEBUG_DAEMON | Debug output from the daemon server. |
MOON_DEBUG_MCP | Debug output from MCP server interactions. |
MOON_DEBUG_PROCESS_ENV | All environment variables passed to the child process (by default only MOON_* keys are logged). Requires --log debug or lower. |
MOON_DEBUG_PROCESS_INPUT | Full stdin passed to the child process. By default moon truncates this. |
MOON_DEBUG_REMOTE | Debug output from remote caching — connection errors, sync status. |
MOON_DEBUG_WASM | Debug output from WASM plugins — loading, execution, memory profiles. |
# Reveal env vars passed to the process (most common debug need)
MOON_DEBUG_PROCESS_ENV=true moon run <project>:<task> --log trace --force
# Full debug output for a failing task
MOON_DEBUG_PROCESS_ENV=true MOON_DEBUG_PROCESS_INPUT=true \
moon run <project>:<task> --log trace --force
# Debug remote caching issues
MOON_DEBUG_REMOTE=true moon run <project>:<task> --log debug
# Debug toolchain installation (use --log debug; no dedicated env var exists)
moon run <project>:<task> --log debug --force
Separate from the debug vars above, moon reads env vars that override configuration — a shell profile or CI environment exporting one of these can make behavior diverge from what the config says. When "the config says X but moon does Y", check for these first:
env | grep '^MOON_'
| Variable | Overrides |
|---|---|
MOON_DAEMON | unstable_daemon — enables/disables the background daemon |
MOON_EXPERIMENT_* | Any experiments.* flag (async graph/affected, hashing, CAS) |
MOON_CACHE | The --cache mode (read, read-write, write, off) |
MOON_CACHE_CAS_MAX_SIZE <sup>v2.5+</sup> | cache.cas.maxSize — CAS eviction limit |
MOON_CACHE_CAS_VERIFY_INTEGRITY <sup>v2.5+</sup> | cache.cas.verifyIntegrity |
MOON_CACHE_SHARED_WORKTREE_CACHE <sup>v2.5+</sup> | cache.unstable_sharedWorktreeCache |
MOON_BASE / MOON_HEAD | Base/head revisions for affected detection |
Tasks can declare env vars that affect both execution and hashing:
tasks:
build:
command: 'vite build'
env:
NODE_ENV: 'production'
Env vars declared in env are included in the hash. If you change NODE_ENV from production to
development, the hash changes and the cache misses.
Env vars not declared in env (but present in the shell) are still passed to the process, but
they don't affect the hash. This means a different NODE_ENV in your shell won't trigger a cache
miss unless it's in the config.
<sup>v2.5+</sup> A task's resolved env can also include variables inherited from a
workspace-level env in .moon/tasks/**/* files, merged beneath the project's own env
(project wins on conflict, unless workspace.mergeStrategies.env changes the strategy). If a
variable has a value that appears nowhere in the project config, check the global task files. See
config-mistakes.md § Workspace-inherited env.
Control verbosity with the --log global option or MOON_LOG environment variable.
| Level | What you see |
|---|---|
off | Nothing |
error | Only errors |
warn | Warnings and above |
info | (default) Status messages, task output |
debug | Internal decisions — hash generation, cache checks, toolchain resolution |
trace | Everything — network requests, child process details, file system operations |
verbose | Like trace plus span information (timing, nesting) |
debug for most issues. As of v2.4 this is the recommended level for day-to-day
debugging — it includes the majority of useful diagnostic information without drowning you in
noise.trace only if debug doesn't reveal the problem. <sup>v2.4+</sup> trace was
made significantly more verbose and is now primarily intended for agents and deep diagnostics —
it may be too spammy for normal debugging, so pipe it to a file.verbose for performance profiling. The span information shows exactly how long each
operation took.# Debug level (recommended starting point)
moon run <project>:<task> --log debug --force
# Trace level, saved to file for analysis
moon run <project>:<task> --log trace --force 2>&1 | tee moon-trace.log
# Or use the MOON_LOG env var
MOON_LOG=debug moon run <project>:<task> --force
# Write logs to a specific file
moon run <project>:<task> --log trace --log-file debug.log --force
These commands let you examine moon's internal state without running tasks.
moon task — inspect resolved task config# Human-readable output
moon task <project>:<task>
# Machine-readable JSON
moon task <project>:<task> --json
Shows the fully resolved task configuration after inheritance, merging, and token resolution. This is the single most useful debugging command — always start here.
Tip: Running moon task <project>:<task> without --json also displays all available PATHs
for the resolved toolchain.
moon project — inspect project metadata# Human-readable output
moon project <project>
# Machine-readable JSON
moon project <project> --json
Shows project metadata: language, toolchain, stack, layer, tags, dependencies, file groups, and all configured tasks.
moon task-graph / moon project-graph — visualize graphs# Visualize the task dependency graph
moon task-graph <project>:<task>
# --dot, or --json for external analysis
# Visualize the project dependency graph
moon project-graph <project>
# --dot, or --json for external analysis
These show task-level and project-level dependency relationships respectively, complementing the lower-level action graph below.
moon action-graph — visualize the dependency graph# Open interactive visualization in browser
moon action-graph <project>:<task>
# Focus on a specific target and include its dependents
moon action-graph <project>:<task> --dependents
# Export for external tools
moon action-graph <project>:<task> --dot > graph.dot
moon action-graph <project>:<task> --json > graph.json
The action graph shows every action moon will take to run a target: toolchain setup, dependency installation, project sync, and task execution. It's the best tool for diagnosing:
moon hash — inspect and compare hashes# Show hash manifest (all sources that generated the hash)
moon hash <hash>
# Compare two hashes
moon hash <hash1> <hash2>
# JSON output
moon hash <hash> --json
For interpreting hash diffs in cache investigations, see
cache-issues.md.
moon query — query project and task information# Find all projects matching criteria
moon query projects --language typescript
moon query projects --stack frontend
# Find all tasks across projects
moon query tasks
moon query tasks --project <project>
# v2.3+: filter tasks by tag
moon query tasks --tags quality
MQL tag vs taskTag (v2.3+): MQL's tag field is a legacy alias for projectTag (project
tags), while taskTag matches task tags. On task queries, taskTag matches the task's own tags,
and projectTag/tag match the task's parent project's tags.
moon query tasks --tags quality # by task tag (regex flag)
moon query tasks --query "taskTag=quality" # by task tag (MQL)
moon query tasks --query "tag=quality" # by parent project tag (alias of projectTag)
moon query projects --query "taskTag=quality" # projects containing a task tagged quality
In v2.3–v2.4, no tag field worked on task queries — they silently matched nothing. Fixed in v2.5; use the
--tagsflag on older versions.
For performance issues that need microsecond-level analysis:
# Generate a trace profile
moon run <project>:<task> --dump --force
This creates a JSON trace file in the current working directory. Open it in:
chrome://tracing and load the fileui.perfetto.devThe trace shows:
This is the most granular debugging tool. Use it when you know something is slow but can't tell what from the logs alone.
The same span data can be exported over OTLP to an observability backend (Grafana, Honeycomb, Jaeger, etc), which is useful for comparing runs over time or debugging CI performance where you can't open a local trace file:
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4318"
moon --otel run <project>:<task>
# Also export log events as OTLP logs (respects --log level)
moon --otel --otel-logs run <project>:<task> --log debug
See the OpenTelemetry docs for transports and the full env var list.
Quick reference for where moon stores internal state:
.moon/cache/
daemon/ # Daemon server state and logs
hashes/<hash>.json # Hash manifest — what was hashed
outputs/<hash>.tar.gz # Archived task outputs (legacy / default)
states/<project>/
snapshot.json # Project snapshot (resolved tasks, config)
<task>/
lastRun.json # Last run metadata (exit code, hash)
stdout.log # Captured stdout from last run
stderr.log # Captured stderr from last run
All paths are relative to the workspace root. The .moon/cache/ directory should be git-ignored.
When
experiments.casOutputsCacheis enabled (v2.3+), new task outputs are stored in a content-addressable store at.moon/cache/manifests/and.moon/cache/blobs/(prefix-sharded by hash; renamed in v2.4 fromac/andcas/) — per-hash.tar.gzfiles stop being created inoutputs/. Seecache-issues.md§ Experimental caching layers.
<sup>v2.5+</sup> Two additions change where to look. With
cache.unstable_sharedWorktreeCacheenabled,blobs/andmanifests/move to the base checkout's.moon/cache(or~/.moon/cache/sharedfor bare clones) — the worktree's own copies may be empty. And with the daemon enabled, archive/hydrate errors are recorded only in.moon/cache/daemon/server.log(moon daemon logs), not in the main process output — a failed hydrate surfaces as a plain cache miss (the task re-runs).
# 1. Check the config first
moon task <project>:<task> --json
# 2. Run with debug logging
moon run <project>:<task> --log debug --force
# 3. If the error is about env vars or missing input
MOON_DEBUG_PROCESS_ENV=true moon run <project>:<task> --log trace --force
# 4. Check stderr from last run
cat .moon/cache/states/<project>/<task>/stderr.log
checks run before the task and emit debug logs. Grep for them to see which check fired:
moon run <project>:<task> --log debug --force 2>&1 | grep -i "check\|condition\|requirement"
# Signals to look for:
# "Running task check" → a check executed
# "Checking requirement" (+ non-zero exit_code) → requirement failed → task fails
# "Skipping task as all conditional checks have passed" → condition skip
# "Will continue to run the task as not all conditional…" → conditions didn't all pass → ran
# "Checking condition" → a condition check executed
# "Task check timed out" → check hit options.timeout
# (silently non-fatal: requirement counts as passing, fingerprint hashes nothing)
See config-mistakes.md § Task checks and cache-issues.md § Fingerprint checks in the hash.
# 1. Inspect the hash
moon hash <hash>
# 2. Force a run and compare hashes
moon run <project>:<task> --force
moon hash <old-hash> <new-hash>
# 3. The diff shows what inputs are missing
# 1. Run twice and capture both hashes
moon run <project>:<task> --force
# note the hash
moon run <project>:<task> --force
# note the new hash
# 2. Diff to see what changed
moon hash <hash1> <hash2>
# 3. The changing field is too volatile — narrow inputs or fix outputs
# 1. Visualize the graph
moon action-graph <project>:<task>
# 2. Look for persistent task nodes with dependents
# 3. Restructure deps so persistent tasks are leaf nodes