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.
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.