Troubleshooting
This page covers common errors, debugging techniques, and solutions for issues you might encounter with mdt.
Common errors
Consumer references a missing source
warning: consumer `installGuide` in readme.md has no matching source
Cause: The target tag references a source name that doesn’t exist in any *.t.md file.
Solutions:
- Check for typos in the block name. Names are case-sensitive —
installGuideandinstallguideare different. - Verify the source is in a
*.t.mdfile. Source tags in regular.mdfiles are ignored. - If you’re in a monorepo, confirm the source is in the same project scope. Providers from a parent or sibling project are not visible across
mdt.tomlboundaries. See Monorepo setups. - Run
mdt listto see all discovered sources and targets.
Argument count mismatch
error: argument count mismatch: provider `badges` declares 1 parameter(s),
but consumer passes 2 argument(s)
Cause: The target passes a different number of arguments than the source declares.
Solutions:
- Count the
:"value"segments on both the source and target tags. - If the source declares
<!-- {@badges:"crate_name"} -->(1 parameter), every consumer must pass exactly 1 argument:<!-- {=badges:"mdt_core"} -->. - See Block Arguments for details.
Duplicate source name
error: duplicate source `install`: defined in `docs.t.md` and `api.t.md`
Cause: Two *.t.md files define a source with the same name. Source names must be unique within a project scope.
Solution: Rename one of the sources, or consolidate them into a single template file.
Stale blocks after editing templates
After editing a source’s content in a template file, all targets referencing that provider become stale. mdt check reports them:
Target block `install` in readme.md is out of date.
Target block `install` in src/lib.rs is out of date.
Solution: Run mdt update to sync all targets. During development, use mdt update --watch to auto-sync on file changes.
Debugging techniques
Use mdt check --verbose
Verbose mode shows the full scan results — how many files were scanned, which sources and targets were found, and which blocks are stale:
mdt check --verbose
Use mdt list to see all blocks
mdt list displays every source and target in the project, their file locations, and their link status:
mdt list
Sources:
@installGuide .templates/template.t.md (2 target(s))
@apiDocs .templates/template.t.md (3 target(s))
Targets:
=installGuide readme.md [linked]
=installGuide crates/my-lib/readme.md [linked]
=apiDocs readme.md [linked]
=orphanBlock docs/old.md [orphan]
Orphaned consumers ([orphan]) indicate missing sources. Providers with (0 target(s)) might be unused.
Use mdt check --diff
When blocks are stale, --diff shows exactly what changed:
mdt check --diff
This produces a unified diff for each stale block, making it easy to see whether the change is expected.
Use mdt update --dry-run
Preview what mdt update would change without modifying any files:
mdt update --dry-run
Dry run: would update 3 block(s) in 2 file(s):
readme.md
src/lib.rs
Cache observability and diagnostics
If cache behavior looks suspicious (unexpected reparses, stale cache artifact, inconsistent local vs CI behavior), use:
mdt info
mdt doctor
mdt info shows cache telemetry:
- Artifact path and schema support
- Hash verification mode
- Cumulative reused vs reparsed file totals
- Last scan summary (
full cache hitvsincremental reuse)
mdt doctor adds cache health checks:
Cache Artifactvalidates readability/schema/key compatibilityCache Hash Modeexplains current fingerprint mode and troubleshooting toggleCache Efficiencywarns when reparses dominate over time
For strict cache-key validation during investigation:
MDT_CACHE_VERIFY_HASH=1 mdt check
This includes content hashes in cache fingerprints (in addition to size/mtime). Disable it again for baseline behavior comparisons.
Formatter interference
Code formatters like dprint, Prettier, and rustfmt can reformat content inside target files, which used to create mdt update → formatter → mdt check loops.
Symptoms
mdt checkreports stale blocks after running a formatter.- Running
mdt updatefollowed by the formatter followed bymdt checkalways shows stale blocks. - Whitespace, wrapping, or markdown table changes keep reappearing.
Recommended fix: configure [[formatters]]
Use formatter integration so mdt formats the full updated file before comparing or writing it. This is the long-term fix for the mdt update → formatter → mdt check cycle described in issue #46.
[[formatters]]
command = "dprint fmt --stdin \"{{ filePath }}\""
patterns = ["**"]
ignore = ["docs/generated/**"]
For per-language tools, add multiple entries:
[[formatters]]
command = "prettier --stdin-filepath \"{{ filePath }}\""
patterns = ["**/*.ts", "**/*.tsx"]
[[formatters]]
command = "eslint_d --fix-to-stdout --stdin --stdin-filename \"{{ filePath }}\""
patterns = ["**/*.ts", "**/*.tsx"]
This makes mdt update and mdt check use the same formatter-aware full-file pipeline.
Formatter-only stale files
Formatter-aware checking can also report formatter-only drift. This happens when the formatter would rewrite the full file, but no individual managed block body is stale.
In that case mdt reports the file in stale_files so automation can distinguish surrounding-formatting drift from block-content drift. The CLI JSON output and MCP responses include stale_files for this reason.
Additional mitigations
Set padding to minimize whitespace differences
Use [padding] in mdt.toml to control the exact whitespace between tags and content. This reduces the surface area for formatter conflicts:
[padding]
before = 0
after = 0
See Configuration for details on padding values.
Match transformer output to formatter expectations
If a formatter enforces specific indentation, configure your transformers to produce output that already matches. For example, if your formatter expects tabs:
<!-- {=docs|trim|indent:"\t"} -->
<!-- {/docs} -->
Exclude template files as a temporary workaround
If you cannot enable [[formatters]] yet, excluding *.t.md from your formatter can still reduce drift. This is a workaround, not the preferred long-term setup.
dprint: Add to dprint.json:
{
"excludes": ["**/*.t.md"]
}
Prettier: Add to .prettierignore:
*.t.md
Use ignore comments for especially formatter-sensitive blocks
If a formatter is still rewriting a specific target block in an undesirable way, use your formatter’s ignore mechanism around that block where appropriate.
You can also exclude whole paths from a specific formatter entry with ignore:
[[formatters]]
command = "dprint fmt --stdin \"{{ filePath }}\""
patterns = ["**/*.md", "!docs/generated/**"]
ignore = ["docs/generated/**", "vendor/**", "!docs/generated/keep.md"]
Leading ! entries act as negation rules in both patterns and ignore.
CI integration issues
mdt command not found
If your CI environment doesn’t have mdt installed globally, install it first:
- name: install mdt
run: cargo install mdt_cli
Or run directly from your workspace without installing:
- name: check docs
run: cargo run --bin mdt -- check
The cargo run approach is slower (it compiles on every run) but avoids installation steps. For faster CI, cache the cargo install or use a pre-built binary.
Check fails but works locally
Common causes:
-
Different working directory. mdt resolves paths relative to where it’s run. Use
--pathto be explicit:- run: mdt check --path ./my-project -
Files not checked out. If your CI does a shallow clone, data files referenced in
mdt.tomlmight be missing. Ensure a full checkout:- uses: actions/checkout@v4 with: fetch-depth: 0 -
Formatter ran after templates changed. If CI runs
dprint fmtbeforemdt check, the formatter might alter target content. Runmdt updateafter formatting, or exclude template content from the formatter.
Recommended CI order
When both formatting and mdt checks are in your pipeline, run them in this order:
- name: format
run: dprint fmt
- name: sync templates
run: mdt update
- name: verify everything is clean
run: |
mdt check
git diff --exit-code
This ensures formatting and template sync are both applied, and the final git diff catches any uncommitted changes.