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.
| Legacy | Canonical | What 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.yaml | Mutable 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.57as the system default. The deterministic-tools chain (detect-signals.sh, brownfieldorchestrator.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 underplugins/gaia/scripts/lib/still benefit fromdeclare -A+mapfile) but no longer required for Tier-2 scanning to function. To upgrade:brew install bashand ensure/opt/homebrew/bin(Apple Silicon) or/usr/local/bin(Intel) precedes/bininPATH. - Scanner tools: install
grype+syft+cdxgen+vulture+semgrepvia Homebrew / pip, OR opt into the Docker runner cascade (brownfield.tools.runner: docker) so the bundledgaia-toolsimage supplies them. - Diagnostic:
gaia-doctorprobes 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-toolsimage 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.
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.