Examples
EN

doctor

Diagnose configuration, template, and structure issues in your Hwaro site.

For content validation (frontmatter, alt text, internal links), use hwaro tool validate.

hwaro doctor

# Check a content directory other than ./content
hwaro doctor -c src/content

# Normalize config values (base_url trailing slash, sitemap priority, …)
hwaro doctor --fix

# Add recommended config sections to config.toml
hwaro doctor --approve

# Do both (equivalent to --fix --approve)
hwaro doctor --full

# Preview changes without writing config.toml
hwaro doctor --full --dry-run

# Output result as JSON
hwaro doctor --json

hwaro tool doctor also works as a backward-compatible alias.

Options

Flag Description
-c, --content-dir DIR Content directory to check (default: content)
-e, --env ENV Check the config as build --env ENV loads it (merges config.ENV.toml; defaults to HWARO_ENV). --fix still edits only config.toml
--fix Perform real fixes — normalize values (base_url trailing slash, sitemap priority, …)
--approve Approve and add recommended optional config sections
--full Both --fix and --approve
--dry-run Preview changes without writing config.toml
--strict Treat warnings as errors when computing the exit code
--max-warnings N Exit non-zero when warning count exceeds N
-j, --json Output result as JSON
-q, --quiet Suppress info output and banner; warnings and errors print one [level] file: message line each on stderr
-h, --help Show help

What It Checks

Config diagnostics:

Template diagnostics:

Content diagnostics:

Structure diagnostics:

Translation report (sites with more than one language):

Pages are paired exactly as the build pairs page.translations (same base name without the language suffix, so about.md, about.en.md and about.ko.md belong together), and only pages a default build publishes count: a draft or future-dated page neither has nor provides a translation. A headless (render = false, also from a section [cascade]) or redirect_to page is never asked for a translation and does not count as one, because the build leaves it out of page.translations and hreflang. Both are info level, so --strict never fails on a partial translation. The human report groups them per language with counts:

Translations:
  ko: 2 missing · 1 without original
  [info] content/about.md: No 'ko' translation
  [info] content/blog/_index.md: No 'ko' translation
  [info] content/notes.ko.md: 'ko' translation has no 'en' original

Each language lists its first 10 issues, then … and N more (use --json for all). In --json every issue is included and carries a language field. Silence either kind with [doctor] ignore.

Example Output

hwaro: doctor

  config.toml
    [ok]   file present & parseable
    [warn] base_url, title
    [ok]   sitemap (changefreq, priority)
    [ok]   taxonomies (duplicates)
    [ok]   search (format)
    [ok]   languages (default_language resolves)
    [ok]   versions (content paths exist)
    [ok]   markdown / pwa (valid enums)
    [ok]   image processing (widths set)
    [ok]   deployment / related (refs resolve)
    [ok]   menus (parent references)
    [ok]   referenced files & dirs
    [ok]   build output (route evidence)
    [ok]   sass (sources & enablement)

  templates/
    [ok]   required files (page.html, section.html)
    [ok]   template syntax

  content/
    [ok]   directory present
    [ok]   front matter (TOML/YAML parse)
    [ok]   front matter menus (declared in config)
    [ok]   front matter templates (exist)
    [ok]   front matter schema ([[content.schema]])
    [info] section index files (_index.md)
    [ok]   translations (pages in every language)

Config:
  [warn] config.toml: base_url is not set

Structure:
  [info] content/docs: Section directory missing _index.md: docs/

checked: 0 errors, 1 warning, 1 info

Tip: Use 'hwaro tool validate' for content checks

A check whose scan never ran renders as [--] … (skipped) rather than as a passing check, for example template syntax when templates/ is missing.

In a color terminal the check lines use ✓/⚠/✗/ℹ glyphs under an hwaro doctor heading, and the summary is a severity-colored ✦ checked outcome line. A clean run ends with checked: no issues found — your site looks great.

Build Output as Evidence

[pwa] offline_page and [pwa] precache_urls are routes, not files. Doctor resolves them against content/ first and, for values that carry an extension such as a compiled stylesheet or a resized image variant, against the last build in [build] output_dir (public/ by default).

That tree has to come from hwaro build. Since Hwaro 0.19, hwaro serve builds into .hwaro/serve/ and leaves output_dir alone, so in a serve-only workflow it is either absent or frozen at an old build. Doctor now says so instead of leaving you to guess:

Both are info level and appear only when the tree actually mattered: a site that references no build-generated path never hears about public/.

Ignoring Known Issues

If doctor reports issues you are aware of and want to suppress, add their rule IDs to the [doctor] section in config.toml:

[doctor]
ignore = [
  "title-default",
  "structure-missing-index",
]

Use hwaro doctor --json to find rule IDs in the output. Ignored issues are completely excluded from both human-readable and JSON output.

ignore only silences warning and info issues. Error-level rules (marked ✗ below) report problems that will fail hwaro build anyway, so listing one cannot disable the CI gate. Doctor keeps reporting it and warns that the entry has no effect.

Available Rule IDs

Rows marked ✗ are error level and cannot be ignored.

ID Category Description
config-not-found config Config file not found ✗
config-parse-error config Failed to parse config ✗
base-url-missing config base_url is not set
base-url-trailing-slash config base_url has trailing slash
title-default config Title is still a scaffold placeholder
sitemap-changefreq-invalid config Invalid sitemap.changefreq
sitemap-priority-range config sitemap.priority out of range
taxonomy-duplicate config Duplicate taxonomy name
language-duplicate config Duplicate language code
search-format-invalid config Unsupported search.format
default-language-undefined config default_language has no [languages.<code>] block
markdown-math-engine-invalid config Unsupported markdown.math_engine
pwa-cache-strategy-invalid config Unsupported pwa.cache_strategy
pwa-display-invalid config Unsupported pwa.display
image-processing-widths-empty config image_processing enabled but widths is empty (silent no-op)
deployment-target-undefined config deployment.target names no [[deployment.targets]]
related-taxonomy-undefined config [related] references an undefined taxonomy
menu-parent-undefined config Menu entry's parent matches no identifier in that menu
version-path-missing config A [[versions.list]] path does not exist under content/
config-path-missing config Referenced file does not exist
config-dir-missing config Referenced directory does not exist
build-output-unusable config [build] output_dir could not validate a route (absent, or hwaro serve output)
build-output-stale config A route was accepted from build output older than the sources
sass-dir-not-scanned config SCSS files under a root sass/ directory, which the build never scans
sass-disabled-with-sources config SCSS entry files under static/ while [sass] is off
missing-config-* config_missing Missing config section (e.g. missing-config-pwa)
template-dir-missing template Templates directory not found ✗
template-required-missing template page.html (or default.html) missing ✗
template-section-missing template section.html missing; sections render with page.html
template-syntax-error template Template fails to parse ✗
template-read-error template Failed to read template ✗
content-dir-missing content Content directory not found
content-frontmatter-invalid content Front matter fails to parse ✗
content-read-error content Failed to read content file ✗
menu-undeclared content Front matter menu name not declared in config
content-template-missing content Front matter template / page_template / [cascade] template names a missing template
content-schema-violation content Front matter violates its [[content.schema]] ✗
structure-missing-index structure Section missing _index.md
translation-missing i18n Page or section has no counterpart in a configured language
translation-orphan i18n Translation has no default-language original

An entry that matches no rule id is reported as having no effect, so a typo in this list never passes silently.

JSON Output

{
  "schema_version": 1,
  "issues": [
    {
      "id": "base-url-missing",
      "level": "warning",
      "category": "config",
      "file": "config.toml",
      "message": "base_url is not set"
    }
  ],
  "summary": {
    "errors": 0,
    "warnings": 1,
    "infos": 0,
    "total": 1
  },
  "exit_code": 0
}