Troubleshooting & common HALTs

This page enumerates the most common error / HALT messages produced by GAIA skills, their canonical workarounds or fixes, and the framework environment variables that operators reach for to debug or unblock a run.

Common HALTs and their workarounds

compliance.ui_present=true but no threat-model.md found (greenfield UI project)

Surface: /gaia-create-arch setup.sh exits 1 in Phase 3 (before any sprint exists).

Cause: The threat-model gate fires whenever compliance.ui_present: true in .gaia/config/project-config.yaml, but on a greenfield project threat-model.md is produced AFTER architecture by design. The advertised --bypass gaia-threat-model --reason "<text>" flag requires a resolvable SPRINT_ID, which doesn't exist pre-sprint-plan — a chicken-and-egg block.

Fix landed: The gate now degrades to a WARNING in the pre-sprint phase (no .gaia/state/sprint-status.yaml, no SPRINT_ID env), and the hard die only fires once a sprint is active — at which point --bypass is also recordable. Upgrade to v1.180.4+ if you still hit this on a fresh project. As a one-off workaround on older versions: export GAIA_STRICT_LIFECYCLE=0 for the single invocation (but this disables every strict-lifecycle gate, not just this one — surgical, not preferred).

HALT: no non-empty test-plan artifact found (post-/gaia-test-strategy)

Surface: /gaia-create-epics setup.sh.

Cause: /gaia-test-strategy --plan writes test-strategy.md under .gaia/artifacts/planning-artifacts/ (the canonical location), and also writes a sibling test-plan.md alias there. On older plugin versions create-epics setup.sh referenced an unbound ${PLANNING_ARTIFACTS} variable and looked only under test-artifacts/ — false HALT.

Fix landed: create-epics setup.sh routes through the shared resolve-artifact-path.sh test_plan helper which puts .gaia/artifacts/planning-artifacts/ at rung 1. Upgrade and retry.

traceability-matrix.md not found at .gaia/artifacts/test-artifacts/strategy/...

Surface: /gaia-dev-story traceability gate.

Cause: /gaia-trace on a greenfield project writes the matrix to the canonical .gaia/artifacts/planning-artifacts/traceability-matrix.md. Older dev-story setup.sh only checked test-artifacts/strategy/ — false HALT.

Fix landed: dev-story setup.sh routes through the shared resolver. Upgrade and retry.

WARNING: story 'X' has sprint_id 'sprint-N' but sprint-status.yaml is missing at ./.gaia/artifacts/implementation-artifacts/sprint-status.yaml

Surface: every /gaia-dev-story transition.

Cause: The canonical home for sprint-status.yaml is .gaia/state/ (the mutable-runtime-state tier). Older transition-story-status.sh hardcoded .gaia/artifacts/implementation-artifacts/ as its default lookup — every transition WARNed and the yaml surface was silently NOT updated.

Fix landed: transition-story-status.sh routes through resolve-artifact-path.sh sprint_status. Upgrade. On older versions, recover state with sprint-state.sh reconcile --sprint-id sprint-N after the fact.

HALT: retro finalize gate sentinel missing — Step 7 (Val sidecar write) must run before finalize

Surface: /gaia-retro finalize.sh under GAIA_FINALIZE_SENTINEL_REQUIRED=1 (default in CI).

Cause: Step 7 (Val sidecar write via val-sidecar-write.sh) is REQUIRED before finalize — it's not optional. The retro skill cannot complete without it.

Fix: Re-run /gaia-retro and let Step 7 execute. Do NOT skip it. See the retro page's Step 7 detail.

payload agent 'gaia:validator' must be 'val'

Surface: /gaia-sprint-review write-val-sentinel.sh.

Cause: The sentinel payload's .agent field must be the literal string "val" (the persona identifier) — NOT the subagent registration name "gaia:validator". The orchestrator MUST set or normalize this before piping the payload.

Fix: Before write-val-sentinel.sh, pipe the Val return through jq '. + {agent: "val"}' (or set it manually in the dispatch wrapper).

auto-rename-migration.sh: HALT: non-interactive auto-rename migration requires --force AND GAIA_MIGRATE_ALLOW_FORCE=1

Surface: /gaia-config-ci auto-rename pass under GAIA_NONINTERACTIVE=1.

Cause: Defense-in-depth for the file-rename security control. Either env var alone is insufficient.

Fix: Pass BOTH --force AND GAIA_MIGRATE_ALLOW_FORCE=1 in the same invocation. Or run interactively (drop GAIA_NONINTERACTIVE).

Statusline doesn't reflect my plugin update

Surface: after running /plugin update, the statusline keeps showing old behavior (no gradient, missing rate-limit countdown, missing dirty-file counts, etc.).

Cause: Plugin updates refresh the cache at ~/.claude/plugins/cache/gaiastudio-ai-gaia-framework/gaia/<version>/, not the standalone runtime install at ~/.claude/gaia-statusline/. The self-heal block lets the install re-copy itself from cache on each render — but the FIRST upgrade onto a pre-self-heal runtime has to be bootstrapped manually.

Fix (one-time per machine):

bash "$(ls -td ~/.claude/plugins/cache/gaiastudio-ai-gaia-framework/gaia/*/scripts/install-statusline.sh | head -1)"
ls -1 ~/.claude/plugins/cache/gaiastudio-ai-gaia-framework/gaia/ | sort -V | tail -1 \
  > ~/.claude/gaia-statusline/.installed-version

After this single run, every future /plugin update self-heals automatically.

Environment variable overrides (operator escape hatches)

These environment variables modify framework behavior for debugging, automation, or one-off bypass. They are intentionally documented here in one place — most are not surfaced on individual command pages.

Variable Effect Where it's read
GAIA_YOLO_FLAG=1 Enables YOLO mode for the current invocation — auto-selects defaults, auto-continues template-output prompts, never auto-bypasses hard gates (Val CRITICAL, 3-attempt fix cap). yolo-mode.sh, consumed by every skill that declares yolo_steps.
GAIA_YOLO_MODE=1 Inherited form for subagent dispatch — set automatically by parent skills when YOLO is active. Treat as read-only. yolo-mode.sh.
GAIA_STRICT_LIFECYCLE=0 Disables ALL strict-lifecycle gates globally (every prerequisite enforcer degrades to WARNING). Very coarse — prefer per-skill --bypass <skill> --reason when possible. lifecycle-strict-mode.sh (consumed by every setup.sh with a strict gate).
GAIA_NONINTERACTIVE=1 Indicates the run has no TTY. The auto-rename security control requires --force AND GAIA_MIGRATE_ALLOW_FORCE=1 on top of this. auto-rename-migration.sh.
GAIA_FINALIZE_SENTINEL_REQUIRED=1 Default ON in CI and most production configs. Makes the Val sidecar write a hard precondition for finalize on every skill that has a sentinel contract (retro, sprint-review, add-feature, etc.). Each skill's finalize.sh.
GAIA_TEST_STRATEGY_NO_AUTOSTUB=1 Disables the auto-stub hydration in /gaia-test-strategy/finalize.sh (which adds empty test_execution + test_execution_bridge stubs to project-config.yaml when missing). Set when you want explicit control of the config. gaia-test-strategy/scripts/finalize.sh.
PROJECT_PATH / GAIA_PROJECT_ROOT / PROJECT_ROOT Project-root override for runtime scripts (used by fixtures and out-of-CWD invocations). Resolution order: CLAUDE_PROJECT_ROOT → GAIA_PROJECT_ROOT → PROJECT_ROOT → PROJECT_PATH → PWD. resolve-artifact-path.sh, sprint-state.sh, dashboards, statusline.
SPRINT_STATUS_YAML=<path> Pin the sprint-status.yaml lookup to an explicit path (bypasses the canonical resolver). Used by bats fixtures. sprint-state.sh, sprint-status-dashboard.sh, transition-story-status.sh.
CI_SETUP_ARTIFACT=<path> Pin the ci-setup.md artifact lookup. By default, when unset, finalize.sh defaults to .gaia/artifacts/test-artifacts/ci-setup.md via the shared resolver — checklist actually runs (was silently skipped pre-fix). gaia-ci-setup/scripts/finalize.sh.

Canonical path cheat-sheet

If a script or doc still says one of the legacy paths on the left, treat that as drift. The canonical paths are on the right.

LegacyCanonicalWhat lives here
docs/planning-artifacts/.gaia/artifacts/planning-artifacts/PRD, architecture, epics-and-stories, threat-model, infrastructure-design, traceability-matrix, test-strategy, test-plan, action-items (write target only — see state tier).
docs/implementation-artifacts/.gaia/artifacts/implementation-artifacts/Story files (per-epic epic-{slug}/{key}-{slug}/story.md), review reports, retros, sprint reviews, sprint archives.
docs/test-artifacts/.gaia/artifacts/test-artifacts/ci-setup.md (only). NOTE: test-plan.md, test-strategy.md, traceability-matrix.md moved OUT of here to planning-artifacts/.
docs/creative-artifacts/.gaia/artifacts/creative-artifacts/Brainstorms, product briefs.
docs/implementation-artifacts/sprint-status.yaml.gaia/state/sprint-status.yamlMutable runtime state — separate "state tier". NOT under artifacts/.
_memory/.gaia/memory/Sidecar decision logs, checkpoints, lifecycle events.
config/.gaia/config/project-config.yaml, test-environment.yaml, test-environment.yaml.example.

Platform requirements

GAIA's orchestration + adapter scripts run on macOS, Linux, and Windows. The portability matrix below documents what each platform needs for a clean lifecycle run.

macOS

  • Bash: macOS ships /bin/bash 3.2.57 as the system default. The deterministic-tools chain (detect-signals.sh, brownfield orchestrator.sh, validate-platform-stack.sh, the dead-code + grype/syft adapters) is bash-3.2 compatible. Bash 4.0+ is recommended for performance (longer-form scripts under plugins/gaia/scripts/lib/ still benefit from declare -A + mapfile) but no longer required for Tier-2 scanning to function. To upgrade: brew install bash and ensure /opt/homebrew/bin (Apple Silicon) or /usr/local/bin (Intel) precedes /bin in PATH.
  • Scanner tools: install grype + syft + cdxgen + vulture + semgrep via Homebrew / pip, OR opt into the Docker runner cascade (brownfield.tools.runner: docker) so the bundled gaia-tools image supplies them.
  • Diagnostic: gaia-doctor probes installation state and prints the achievable scan tier with concrete remediation commands.

Linux

  • Bash: distros ship bash 4+ or 5+; no special configuration required.
  • Scanner tools: same options as macOS — host PATH install, or the Docker runner cascade.
  • CI: the canonical CI matrix is Ubuntu LTS; the bats suite under plugins/gaia/tests/ assumes bash 4+ by default on Linux runners.

Windows

  • Recommended path: WSL2 + Ubuntu. Install WSL2 (wsl --install), then a current Ubuntu image. GAIA runs natively in the WSL2 shell — same behavior as Linux.
  • Alternative path: Git Bash. Git for Windows ships a recent bash that runs the deterministic-tools chain. Some adapter scripts assume POSIX tools (find, sed -E, awk) — Git Bash provides all of these.
  • Not supported: raw Windows cmd.exe / PowerShell. The orchestration scripts are bash-only by design. A future port may add a PowerShell entrypoint, but the current product surface assumes a POSIX shell.
  • Scanner tools: the Docker runner cascade is the cleanest path on Windows — the bundled gaia-tools image supplies grype/syft/cdxgen/vulture/semgrep without needing each one installed on the host. Enable via /gaia-config-brownfield (brownfield.tools.runner: docker) after Docker Desktop is running.

Cross-platform CI

The plugin's CI matrix runs the bats suite on ubuntu-latest, macos-latest, and windows-latest (Git Bash). Regressions in bash 3.2 portability or POSIX-tool assumptions fail the macOS / Windows jobs before merge.

Statusline issues

Statusline doesn't refresh after a plugin update

See above for the one-time bootstrap. The self-heal logic means subsequent updates self-heal automatically — but the FIRST upgrade onto a pre-self-heal runtime needs a manual install-statusline.sh run.

Statusline shows a stale update arrow after the plugin already updated

The update detector compares the installed semver to the latest-release-cached semver. A stale arrow that lingers after an update was fixed (semver-aware "> not ≠"). Upgrade to v1.180.3+.

Rate-limit display shows ".../.." for one or both windows

The runtime hides missing windows by design — Pro/Max accounts only see the rate-limit JSON after their first API response in the session. Wait for a few interactions; the field will populate.

Stray .gaia/memory/ directories in subdirectories

An older statusline wrote the context-window snapshot under whatever directory the session's CWD happened to be, creating stray .gaia/memory/.context-window-snapshot.json files across the project tree. This was fixed: the snapshot now anchors to the session's project root. Run /gaia-statusline-refresh after upgrading so the installed runtime picks up the fix. To find existing strays, run /gaia-doctor — it reports each stray path with a removal command but never deletes anything itself.

Design lifecycle issues

Design gate blocks solutioning commands

Phase-three entry points (/gaia-dev-story, /gaia-create-arch, /gaia-create-epics, /gaia-readiness-check, /gaia-adversarial, /gaia-threat-model, and others) halt with “Design is not approved” when the design record is not in the approved state. To unblock: run /gaia-design-review to complete the approval round, or pass --force-design --reason "<text>" for an audited override. Projects without a UI (compliance.ui_present: false or absent) skip the gate automatically.

Claude Design integration unavailable

Design skills halt with “The Claude Design integration could not be reached” when DesignSync is absent or unreachable. Use a Claude Code session that exposes the DesignSync tool surface. If the message says “unauthorized” instead, run /design-login (API-token sessions) or grant design access when prompted (claude.ai sessions).

Design artifact surface unavailable

The product design project requires the Artifact tool. If it cannot be reached, skills halt with “The Design artifact surface (Artifact tool) could not be reached.” Ensure the Artifact tool is available in your session.

Convergence reports “vacuous”

/gaia-design-review halts if no stakeholder is tagged with design or ux. Create an approver with /gaia-create-stakeholder using a design or ux tag.

Product design project not set up

Republish steps in /gaia-edit-ux and /gaia-add-feature halt with “The product design project is not set up.” Run /gaia-create-ux to create and bind a product design project.

Sprint-id mismatch on close

/gaia-sprint-close and other sprint-state writers refuse to write when the caller's sprint id does not match the one in sprint-status.yaml. The error message names both ids. Verify the active sprint with /gaia-sprint-status and use the correct id.

Test Execution Bridge issues

/gaia-bridge-enable never completes

The test-environment.yaml.example file under .gaia/config/ carries an explicit GAIA-MANIFEST-TEMPLATE guard line that intentionally fails Layer 0 readiness until the file is renamed to test-environment.yaml AND the template guard is removed. The bridge skill installs the .example for you (idempotent), but does NOT auto-promote it — that requires operator intent (you pick + edit runners). See /gaia-bridge-enable.

Reviews report --execution-evidence missing

Make sure the bridge is enabled (test_execution_bridge.bridge_enabled: true in .gaia/config/project-config.yaml) AND a test-environment.yaml manifest exists with runners that the bridge can dispatch. Without both, reviews can write PASSED without execution evidence and the gate refuses to accept the verdict.