website/docs/guides/debug-task.mdx
import VersionLabel from '@site/src/components/Docs/VersionLabel';
Running tasks is the most common way to interact with moon, so what do you do when your task isn't working as expected? Diagnose it of course! Diagnosing the root cause of a broken task can be quite daunting, but do not fret, as the following steps will help guide you in this endeavor.
We provide an AI agent skill called debug-task that helps you systematically diagnose failing or
misbehaving moon tasks. If you've ever spent time hunting down why a task is producing stale
results, getting unexpected cache misses, or behaving differently in CI, this skill is for you.
To install this skill, run the following command:
$ npx skills add moonrepo/moon --skill debug-task
The skill walks you through a 5-step diagnostic workflow:
It covers 15+ specific problem types across cache issues, configuration mistakes, execution problems, and CI vs local discrepancies. Most issues resolve by step 3, with deeper reference material available on demand.
To use it, simply describe your task problem in Claude Code, Codex, OpenCode, or another compatible AI agent — mention the task target, what you expected, and what actually happened. The skill will take it from there.
/debug-task Help me debug why the `app:build` task is not being cached
Before we dive into the internals of moon, we should first verify that the task is actually
configured correctly. Our configuration layer is very strict, but it can't catch everything, so jump
to the moon.* documentation for more information.
To start, moon will create a snapshot of the project and its tasks, with all tokens
resolved, and paths expanded. This snapshot is located at
.moon/cache/states/<project>/snapshot.json. With the snapshot open, inspect the root tasks
object for any inconsistencies or inaccuracies.
Some issues to look out for:
command and args been parsed correctly?inputFiles, inputGlobs, and inputVars expanded correctly from inputs?outputFiles and outputGlobs expanded correctly from outputs?toolchains correct for the command? If incorrect, explicitly set the
toolchain.options and state correct?:::info
Resolved information can also be inspected with the moon task <target> --json
command.
:::
If the configuration from the previous step looks correct, you can skip this step, otherwise let's
verify that the inherited configuration is also correct. In the snapshot.json file, inspect the
root inherited object, which is structured as follows:
config - A mapping of configuration files that were loaded, in order. Each config represents a
partial object (not expanded or resolved). Only files that exist will be mapped here.layers - A mapping of task IDs to configuration files that have been inherited.If configuration looks good, let's move on to inspecting the trace logs, which can be a non-trivial amount of effort. Run the task to generate the logs, bypass the cache, and include debug information:
MOON_DEBUG_PROCESS_ENV=true MOON_DEBUG_PROCESS_INPUT=true moon run <target> --log trace --force
Once ran, a large amount of information will be logged to the terminal. However, most of it can be ignored, as we're only interested in the "is this task affected by changes" logs. This breaks down as follows:
git status --porcelain --untracked-files (from the moon_process module).git ls-files --full-name --cached --modified --others --exclude-standard <path> --deduplicate
command (also from the moon_process module). This command can also be ran locally to verify the
output.git hash-object
command. If you passed the MOON_DEBUG_PROCESS_INPUT environment variable, you'll see a massive
log entry of all files being hashed. This is what we use to generate moon's specific hash.If all went well, you should see a log entry that looks like this:
moon_task_runner::task_runner Generated a unique hash task_target="<task>" hash="<hash>"
The important piece is the hash, which is a 64-character SHA256 hash, and represents the unique hash of this task/target. This is what moon uses to determine a cache hit/miss, and whether or not to skip re-running a task.
Let's copy the hash and move on to the next step.
With the hash in hand, let's dig deeper into moon's internals, by inspecting the hash manifest at
.moon/cache/hashes/<hash>.json, or running the moon hash command:
moon hash <hash>
The manifest is JSON and its contents are all the information used to generate its unique hash. This information is an array, and breaks down as follows:
deps
and inputs.
git hash-object) this task requires to run.package.json
dependencies.Cargo.toml
dependencies.Some issues to look out for:
deps and implicitDeps?inputs and
implicitInputs? If not, try tweaking the config.Another avenue for diagnosing a task is to diff the hash against a hash from a previous run. Since we require multiple hashes, we'll need to run the task multiple times, inspect the logs, and extract the hash for each. If you receive the same hash for each run, you'll need to tweak configuration or change files to produce a different hash.
Once you have 2 unique hashes, we can pass them to the moon hash command. This
will produce a git diff styled output, allowing for simple line-by-line comparison debugging.
moon hash <hash-left> <hash-right>
Left: 0b55b234f1018581c45b00241d7340dc648c63e639fbafdaf85a4cd7e718fdde
Right: 2388552fee5a02062d0ef402bdc7232f0a447458b058c80ce9c3d0d4d7cfe171
[
{
"command": "build",
"args": [
+ "./dist"
- "./build"
],
...
}
]
This is extremely useful in diagnoising why a task is running differently than before, and is much easier than inspecting the hash manifest files manually!
If you've made it this far, and still can't figure out why a task is not working correctly, please ask for help!