# Troubleshooting gstack messages Find the message you saw, then follow its fix. Every check gstack runs ends in one of three states: - **ran**: the check executed. It may still have found problems. - **not run**: gstack chose not to run it (you turned it off, or nothing applied). - **unavailable**: gstack tried and could not run it. This is missing coverage, never a pass. A message that is not `ran` has this shape: ``` : not run (). Fix: unavailable: (). No review ran; this is missing coverage, not a pass. Fix: : ran, verdict unverified (). Fix: read the output above ``` Search this page for the words after `unavailable:` or `not run (`. Each section has a stable link anchor; the reason codes and anchors come from `lib/gate-outcomes.ts`, and a free test fails if a code has no section here. ### Check readiness before a skill runs Run `~/.claude/skills/gstack/bin/gstack-doctor` (other hosts: `./setup --status` in your gstack checkout prints the doctor's absolute path). It prints one row each for the install, state root, Bun, hooks, Codex, the cached Codex model check, artifacts sync, the browse bundle, Claude Code, the largest session journal and recent /autoplan guard codes. Each row is `ok`, `warn`, `not configured` or `fail` with its fix; only `fail` exits non-zero. It makes no paid call: the Codex rows report the cached model check and its age, and `--live` runs that check once. Paste its output into bug reports. --- ## Model policy The [model-policy guide](model-policy.md) explains the two tiers, all six settings, precedence and the read-only inspection command. A resolved selection is not proof that the requested provider or model can run. ### Model policy configuration is invalid or unreadable **Meaning.** The new policy path could not read a coherent configuration or a configured value is invalid. An unreadable file is not treated as an absent setting: doing that could silently select the premium default. **Fix.** Repair the file or key named by the error. `plan_review_tier` accepts `frontier`, `smart` or `host`; `implementation_tier` accepts `frontier` or `smart`. Use `gstack-config unset ` to restore a default, not an empty value. A rejected `set` leaves the old value intact. Correct ownership/read permissions rather than deleting an unrelated state directory. For a malformed line such as `plan_review_tier = smart`, edit the reported file and correct or remove that line first: `unset` only removes canonical `key:` records and cannot repair a misspelled delimiter or quoted key. **Expected result.** `gstack-models list` or the corresponding `resolve` command explains the winning source. No outside model call ran on this error. ### A custom provider needs an explicit model or host mode **Meaning.** Native settings or environment flags route the reviewer to a custom endpoint or partner platform. A public API catalog identifier is not necessarily a valid deployment identifier there. Malformed native provider configuration also prevents gstack from assuming the public endpoint. **Fix.** Supply a compatible model for that request, set the provider's gstack environment override or tier-model setting, or choose `gstack-config set plan_review_tier host`. Keep credentials in the provider's normal authentication mechanism, never in a model setting. If the error names a malformed native configuration, fix that source first. **Expected result.** Inspection reports an explicit selection or honest host-managed delegation. It does not certify account entitlement or send a public default to an inferred custom endpoint. ### The selected review model could not be used **Meaning.** The requested model was rejected or the provider could not complete the review. A model may be nonexistent, unavailable to the account, retired, or behind a misconfigured endpoint; preserve the provider's evidence rather than treating all of these as the same diagnosis. No successful outside review is implied, and gstack does not substitute a different model. **Fix.** Change the source named in the error. For example, an environment override beats a tier setting, so editing the tier cannot replace it. Unset or correct that override, or explicitly name a supported model for the request. Follow the existing auth, quota, sandbox or timeout repair when that is the reported failure. Missing usage identity remains unknown, even if inspection showed a requested ID. **Expected result.** The next invocation announces its model and source, checks that same selection and reports the actual provider result. A cached probe or a native fallback is not a completed outside review. ### Model-catalog freshness is unknown, stale or needs an update **Meaning.** Official evidence changed, a required source could not be checked, or the retained evidence does not match this catalog and parser. `unknown/source-unavailable` is not current. A later source failure must not erase an earlier retirement finding or close its maintenance issue. **Fix.** Read the failed source and evidence details in the freshness workflow report and tracking issue. Repair the source parser or publication access when needed, then run the workflow manually against the default branch. If a model was superseded or deprecated, follow the [maintainer checklist](model-policy.md#maintainer-update-checklist) to qualify a replacement or record a supported mitigation. Do not clear the issue just because a new release has not been adopted yet. **Expected result.** Complete source checks produce evidence bound to the current catalog, while unresolved lifecycle findings remain visible. Personal pins and unrelated PRs are unchanged. Check the workflow's last run date too: a scheduled job that never runs cannot report its own absence. #### Repairing `recoveryRequired` state This flag deliberately keeps the issue open until a maintainer repairs the record. Another successful fetch alone cannot certify corrupted history. 1. Save the current issue body, including its retained recovery archive, and download the latest trustworthy workflow report. Use its `report.state` as the starting state; do not invent run IDs, catalog hashes or check dates. 2. Reconcile its `lifecycle` array with the issue's independent lifecycle block and any retained archive. Preserve known retirements, deadlines and human dispositions. A model currently in the catalog must not keep an old `resolvedByCatalog` flag from a previous replacement. 3. In the issue editor, restore valid JSON between the `state:begin` and `state:end` marker lines. Put that same lifecycle array between `lifecycle:begin` and `lifecycle:end`. Restore the standalone ownership and region markers, and leave human text outside the owned region intact. 4. Set `recoveryRequired` to `false` only after that reconciliation. If prior successful evidence cannot be verified, set `lastSuccess` to `null` rather than making up a fresh success. If lifecycle history cannot be recovered, leave the flag set and the issue open for investigation. 5. Run **Model-policy freshness** against the default branch. Only complete, current evidence with no unresolved findings can close the issue. A parsing or source error leaves it open; do not clear history to manufacture a pass. ## Outside reviews (Codex and Claude Code) Outside reviews send your diff, plan or question to a second AI provider. Their verdict comes from `lib/outside-review-result.ts`, which prints `VERDICT: clean|findings|unverified|unavailable`. A `findings` verdict with a P0 or P1 finding blocks exactly like a native P0/P1. `unverified` and `unavailable` are missing coverage: /ship and /review continue, show the gap in the readiness dashboard and the PR body, and never count it as a pass. ### How the verdict is read from a review - **Severity tags.** `[P0]`-`[P3]` (or Codex's native `P1:` labels). P0 and P1 block; P2 and P3 are advisory. - **Severity words in label position.** `Severity: High`, `Priority: low`, a line that starts with `High:`, `High —`, `[High]` or `**High**` (after an optional heading, bullet or number), a bold `**High**` anywhere, or a table cell `| high |`. Critical and High block like P0 and P1; Medium and Low are advisory like P2 and P3. Words inside prose do not count: "high-level", "low-risk", "a medium-term follow-up" and "no critical findings" are not findings. - **No findings.** A review with no tag and no label is `clean` only when it says so explicitly (`NO_FINDINGS`, "no issues", "did not find any bugs"). Otherwise it is `unverified` (see below), never `clean`. - **Design proposals** (the design-direction voices in /design-consultation and /office-hours) are read with the `proposal` gate: a completed proposal needs only its `Recommendation: ... because ...` line. Outside-review prompts ask the reviewer to label each finding Critical, High, Medium or Low, so most reviews land on `findings` or `clean`. ### `gstack: cannot load gstack-codex-probe` / `gstack: cannot locate (shell: ...)` / `CODEX_MODE: helper_unavailable` **Meaning.** Skills run `gstack-codex-probe` as a command, one subcommand per check, so the shell your agent uses does not matter. `cannot load gstack-codex-probe` and `CODEX_MODE: helper_unavailable` mean the probe file is missing or not executable in your install. `cannot locate (shell: ...)` comes from a helper that is still loaded into your shell with `source` (`gstack-egress-lib.sh`, and the Codex probe when a skill rendered before the upgrade sources it). Such a helper finds its own directory from bash (`BASH_SOURCE`) or zsh (`%x`). In any other shell (dash, sh), or when the shell cannot say which file it is reading, it stops instead of guessing a path. The message names the shell it saw. **Fix.** For `cannot load`, re-run `./setup` from your gstack checkout (or `/gstack-upgrade`). For `cannot locate`, run the skill from bash or zsh (the macOS and Linux defaults), or tell the helper where gstack is installed: ```bash export GSTACK_ROOT=~/.claude/skills/gstack # your install dir; it holds bin/ ``` If you see `gstack: sourcing gstack-codex-probe is deprecated ...`, a skill rendered before the upgrade is still sourcing the probe. It keeps working until a release on or after 2026-10-21; run `/gstack-upgrade` to re-render your skills now. **Expected result.** `~/.claude/skills/gstack/bin/gstack-codex-probe select-model exec` prints `CODEX_SEL: ` (and `CODEX_MODEL: (exec; source: ...)` on stderr) from any shell, and preflights print a `CODEX_MODE` other than `helper_unavailable`. `gstack-codex-probe help` lists every subcommand. ### `Codex outside review unavailable: Codex's sandbox could not start here (...)` **Meaning.** Codex runs every command inside a Linux sandbox (bubblewrap). In many containers and devcontainers the kernel does not allow unprivileged user namespaces, so the sandbox cannot start and every command Codex tries fails. gstack reports this instead of trusting a review that read nothing. `CODEX_MODE: sandbox_unavailable` is the same condition found by the free preflight before any paid call. **What is kept.** Nothing was sent for review, or the review was discarded. Your code and files are unchanged. **Fix.** Enable unprivileged user namespaces for the container (for Docker, a seccomp profile that allows them), or, inside a container you trust, run Codex without its sandbox for this shell: ```bash export GSTACK_CODEX_NO_SANDBOX=1 ``` **Expected result.** With namespaces enabled, the next review prints `OUTSIDE_STATUS: completed`. With `GSTACK_CODEX_NO_SANDBOX=1`, every review prints `WARNING: GSTACK_CODEX_NO_SANDBOX=1: ...` because Codex can then read and write anything your user can. Only the exact value `1` works, and only from your shell environment. ### `... outside review unavailable: the reviewer could not run commands or read the diff (...)` **Meaning.** The reviewer tried to run commands but none succeeded, or its answer says it could not run commands or read the diff. A "no issues found" written after that is not a review. **What is kept.** Nothing; the answer is shown above but not counted. **Fix.** Read the reviewer's stderr above, repair what it names, and re-run. **Expected result.** `OUTSIDE_STATUS: completed` with a `VERDICT:` line. ### `... outside review unavailable: the reviewer process failed (exit N: ...)` **Meaning.** The provider CLI exited non-zero. The first stderr line is in the parentheses. **What is kept.** Partial output is shown above; it is not counted. **Fix.** Repair the cause in the provider diagnosis (log in again, fix the model, check the network), then re-run. **Expected result.** `OUTSIDE_STATUS: completed`. ### `... outside review unavailable: the reviewer hit its time limit and was stopped (exit 124)` **Meaning.** The provider did not finish within its deadline and was stopped. **What is kept.** Partial output is shown above; it is not counted. **Fix.** Re-run with a smaller scope, or check the provider's status and your network. **Expected result.** `OUTSIDE_STATUS: completed`. ### `... outside review unavailable: the reviewer returned no response` **Meaning.** The provider exited successfully but returned nothing. **Fix.** Read the stderr above, then re-run. **Expected result.** `OUTSIDE_STATUS: completed`. ### `... outside review unavailable: the reviewer declined to review` **Meaning.** The provider answered with a refusal instead of a review. **Fix.** Re-run; if it declines again, rely on the native review. **Expected result.** `OUTSIDE_STATUS: completed`. ### `... outside review unavailable: the response lacks the markers this gate requires (...)` **Meaning.** The answer did not contain what the gate checks: a `Recommendation: ... because ...` line, a `SCORE:`/`AMBIGUITIES:` pair, or severity tags. **Fix.** Re-run the review. **Expected result.** `OUTSIDE_STATUS: completed`. ### `... outside review: ran, verdict unverified (...)` / `OUTSIDE_STATUS: unverified` / `GATE: UNVERIFIED` **Meaning.** The review completed but had no severity tag or label and gave no explicit no-findings conclusion, so no pass or fail can be read from it ([how the verdict is read](#outside-review-verdict)). **What is kept.** The full answer is shown above. **Fix.** Read the output above and decide. It is not a pass, and /ship and /review list it as missing coverage. ### `CODEX_MODE: model_unusable` / `MODEL_UNUSABLE` **Meaning.** Codex rejected the selected model: the account cannot use it (HTTP 400), the model is retired, or a custom provider's `base_url` is wrong (HTTP 404). The `CODEX_MODEL:` line names the model and where it came from. **Fix.** Choose a model your account can use, or correct the provider: ```bash export GSTACK_CODEX_MODEL= # or edit model / base_url in ${CODEX_HOME:-~/.codex}/config.toml ``` **Expected result.** `CODEX_MODE: ready`. ### `CODEX_MODE: quota_exhausted` / `MODEL_QUOTA_EXHAUSTED` **Meaning.** Codex refused the call because the account behind it hit its usage limit (`You've hit your usage limit`, or `insufficient_quota`). The line under the marker is Codex's own message, with its reset time and where to buy more. The model choice is fine. gstack reports outside coverage as unavailable, never as a pass, and caches the result for 15 minutes, so the rest of the run (and other skills) make no Codex call. The HINT line says how many minutes remain. **Fix.** Wait for the reset time in Codex's message, or add credits or a higher plan for that account. To use a different account, sign in again: ```bash codex login ``` To re-check before gstack's 15-minute cache expires (for example, right after buying credits), skip the cached result for one check, or delete it: ```bash export GSTACK_CODEX_PROBE_RETRY=1 # unset it again afterwards # or rm -f ~/.gstack/.codex-model-probe # /.codex-model-probe ``` **Expected result.** After the reset (or after `codex login`, which changes the auth signature and re-probes at once), `CODEX_MODE: ready`. ### `CODEX_MODE: unverified (rate_limited)` / `MODEL_PROBE_RATE_LIMITED` / `unavailable: Codex rate-limited the review` **Meaning.** Codex answered HTTP 429 (too many requests), which usually clears within seconds. It is a different state from `quota_exhausted` and is never cached. At probe time the review still runs, and its own result decides. A 429 during the review itself means that review failed, so coverage is missing, never a pass. **Fix.** Re-run the review in a minute. If 429s persist, check the rate limits for the account or API key on the provider's dashboard. **Expected result.** `CODEX_MODE: ready`, and the review completes. ### `CODEX_MODE: unverified` / `MODEL_PROBE_INCONCLUSIVE` **Meaning.** The short model check timed out or hit a network error, so gstack could not confirm the model works. The review still runs and its own result is checked. **Fix.** Nothing now. If the review then fails, its message says why. ### `CODEX_MODE: not_authed` / `AUTH_FAILED` **Meaning.** No Codex credentials were found: no `CODEX_API_KEY`, no `OPENAI_API_KEY`, no `auth.json`, and no set environment variable named by a custom provider's `env_key` in `config.toml`. **Fix.** ```bash codex login ``` or export the variable your provider's `env_key` names. **Expected result.** `CODEX_MODE: ready`. ### `Codex review skipped (codex_reviews disabled)` / `CODEX_MODE: disabled` **Meaning.** You turned outside reviews off. This is `not run`, never a pass and never an outage. **Fix.** To turn them back on: ```bash gstack-config set codex_reviews enabled ``` ### `NOTICE: gstack outside reviews send the review prompt and code to Codex (...) using ...` **Meaning.** Outside reviews are on by default. The first one on a machine says which provider receives your prompt and code and which login or key pays for it. It shows once (gstack records `.codex-review-notice-shown` in its state directory) and never blocks the review. **Fix.** Nothing, if that is what you want. To stop sending code to Codex: ```bash gstack-config set codex_reviews disabled ``` **Expected result.** Later reviews print `Codex review skipped (codex_reviews disabled)` or `CODEX_MODE: disabled`, and /ship and /review show the outside review as not run. --- ## /ship, /review and /document-release ### `Shipped without a version change: no version source is configured (no VERSION file, no .gstack/version-path). ...` **Meaning.** /ship found no version file it owns: there is no `VERSION`, no `.gstack/version-path`, or release automation (release-please, Changesets, semantic-release, a workspace monorepo, a placeholder package.json version) owns the version. This is `not run` for the version bump. **What is kept.** Everything else ships. No bump, no CHANGELOG version header, no `vX.Y.Z` PR title prefix. **Fix.** To have /ship version releases, create `VERSION`, or pin the file: ```bash echo package.json > .gstack/version-path && git add .gstack/version-path ``` **Expected result.** The next /ship bumps that file and writes a CHANGELOG header. ### `gstack-version-bump: classify: version source is broken: ...` **Meaning.** The pinned version file does not exist, is empty, unreadable, or contains no parsable version. /ship stops instead of inventing `0.0.0.0`. **What is kept.** Nothing was written. **Fix.** Repair that file, or correct `--version-path` / `.gstack/version-path`. **Expected result.** `gstack-version-bump classify` prints the current version. ### `BASE_REFRESH: stale ` / `Base coverage: stale at ` **Meaning.** /review could not fetch the base branch (offline, no credentials, or a read-only `.git` such as Codex's sandbox), so it reviewed against your local `origin/` at ``. **Fix.** Run `git fetch origin ` outside the sandbox and rerun /review if the base has moved. ### `gstack-wtree: cannot create a temp index under ; set TMPDIR to a writable directory` / `gstack-review-log: cannot create a private object directory under ; no working-tree fingerprint. Set TMPDIR to a writable directory.` **Meaning.** /review fingerprints the working tree in a private temporary object directory (Codex's sandbox keeps `.git` read-only). The temp directory could not be created, so this review has no fingerprint and later steps cannot reuse its evidence. **Fix.** `export TMPDIR=`, then run /review again. ### `gstack-wtree: cannot stage the working tree: ` **Meaning.** The fingerprint's objects could not be written. Standalone `gstack-wtree` writes into the repository's object store. **Fix.** Run it through /review (`gstack-review-log` supplies a private object directory), or make `.git/objects` writable. ### `Plan completion audit: not run (no plan is bound to this branch and no docs/designs/ file matches). Fix: ...` **Meaning.** /ship and /review audit a PR against the plan bound to its branch (plan mode, /autoplan, or a `Plan: ` line in the PR body). With none bound and no matching `docs/designs/` file, they no longer pick the newest unrelated plan; the audit is `not run`. **Fix.** Add `Plan: docs/designs/.md` to the PR body, or run /autoplan. **Expected result.** The next /ship or /review prints the audit against that plan. ### `gstack-learnings-search: bun not found on PATH, so learnings could not be read. Fix: ...` **Meaning.** Learnings search (and `gstack-timeline-read`, "the timeline") needs Bun. It exits 127 instead of looking like "no learnings". **Fix.** Install Bun from https://bun.sh, then `cd && ./setup`. **Expected result.** Skills print your prior learnings again. ### `LEARNINGS: unavailable ()` **Meaning.** A skill's learnings lookup failed (often Bun is missing). Skills used to show nothing here, which looked like "no learnings". **What is kept.** Your learnings file is untouched; the skill continues without them. **Fix.** Read the reason in parentheses. If it names Bun, install it and re-run `./setup`. ### `gstack-learnings-log: could not create a temp file in , so the learning was not recorded. Fix: ...` **Meaning.** The learnings, question and preference logs write through a temp file in `${TMPDIR:-/tmp}`. A sandbox that blocks it used to drop the entry silently. **Fix.** `export TMPDIR=` and log again. ### `CALIBRATION: not calibrated: no recorded signals` **Meaning.** Your logged answers matched no psychographic signal, so /plan-tune has nothing to calibrate from. It no longer says "calibrated". **Fix.** Answer more registered questions, then `gstack-developer-profile --derive`. ### `gstack: earlier data for this project is in projects/ (a worktree's old bucket). Merge it: ...` **Meaning.** Linked worktrees now share the main checkout's project identity. Learnings and checkpoints a worktree wrote under its own old bucket still exist. **Fix.** Merge them (logs are de-duplicated, conflicts listed, nothing overwritten): ```bash gstack-slug --adopt-legacy --from ``` Or stop the reminder: `gstack-slug --adopt-legacy --dismiss `. --- ## Design skills ### `DESIGN_NOT_AVAILABLE: --version exited ` (or `timed out after 10s`, `is not installed`, `no timeout/gtimeout/perl ...`) **Meaning.** Design skills now run `"$D" --version` with a 10-second deadline before claiming `DESIGN_READY`. Exit 137 means macOS killed the binary at launch (a bad code signature); setup prints the same condition as `design unavailable: is killed at launch (exit 137) after re-signing`. **What is kept.** Nothing was generated or spent. **Fix.** Re-run setup, which rebuilds and re-signs the binary: ```bash cd && ./setup ``` If the message names a missing timeout tool, install coreutils (`gtimeout`) or perl. **Expected result.** The next design skill prints `DESIGN_READY`. ### `GSTACK_DESIGN_IMAGE_MODEL="" is not a gpt-image model name (expected something like gpt-image-2); fix it or unset it to use gpt-image-2` **Meaning.** The design binary refuses an image tool model override that is not a `gpt-image-*` model name, before sending any request. **Fix.** `export GSTACK_DESIGN_IMAGE_MODEL=gpt-image-2` (or another gpt-image model your key can use), or `unset GSTACK_DESIGN_IMAGE_MODEL`. ### `TASTE_PROFILE_UNAVAILABLE: could not resolve the project slug (gstack-slug failed). Fix: run ./setup.` **Meaning.** /design-consultation and /design-shotgun load your taste profile. This says it could not be found, instead of acting as if you had none. **Fix.** `cd && ./setup`. ### `cannot save paid image to : (). Image bytes were received; saved a recovery copy to . Fix: ...` **Meaning.** The paid image generation succeeded but writing it to the output path failed (disk, permissions, or every name through `-999` taken). gstack never buys the same image twice and never overwrites an existing image. **What is kept.** The image, at the printed recovery path in your temp directory. **Fix.** Follow the printed fix (free disk space, make the folder writable, or pick a new `--output`), then copy the recovery file to where you want it. --- ## Codex and other env-var hosts ### `gstack: no install found (tried ). Fix: ./setup --host from your gstack checkout; ./setup --status shows it.` **Meaning.** On Codex, Factory, OpenCode, Cursor, Copilot and Kiro every bash block finds gstack on its own: an exported `GSTACK_ROOT` (with `bin/` and `lib/`), then a repo-local install, then the host's global install (`$CODEX_HOME/skills/gstack` on Codex). None existed. **Fix.** ```bash cd && ./setup --host codex # or your host ./setup --status ``` **Expected result.** `./setup --status` lists the install and says the router is a real file and its section links resolve. ### `router is a symlink, which Codex skips` / `section links: N of M broken (first: ...)` **Meaning.** `./setup --status` checks each registered install. Codex ignores a symlinked SKILL.md, and a broken section link means a carved skill cannot load its next step. **Fix.** `cd && ./setup --host ` (the command is printed). ### `saved your edited to (setup rewrites gstack's SKILL.md copies on every run)` **Meaning.** Installed router and nested SKILL.md files are now real copies, rewritten by every setup. Your hand edit was saved first. **What is kept.** Your edit, at ``. Move the customization into your own skill. ### `error: could not back up an edited SKILL.md copy under ; the previous runtime root is unchanged` **Fix.** Fix permissions on `/backups`, then `./setup --host `. ### `ERROR: is not a gstack checkout; nothing was changed. Re-run Step 2.` / `INSTALL_DIR is not set: re-run Step 2 ...` **Meaning.** /gstack-upgrade now checks, before any git command, that it is inside gstack's own checkout. A block ran without the path Step 2 printed, so it stopped instead of touching your project. **Fix.** Run Step 2 again and prefix the block with `INSTALL_DIR=`. ### `//SKILL.md is bytes, over the 160,000-byte limit. Fix: carve sections with usesLazySections() for this skill.` **Meaning.** For contributors: hosts read at most 160,000 bytes of one SKILL.md. `bun run gen:skill-docs` fails in the repo and warns under `--install-root`. --- ## Setup and auto-update ### `gstack needs Bun 1.3.3 or newer (1.4.2 recommended); found at . Nothing was installed or changed.` **Meaning.** Bun older than 1.3.3 silently ignores the build flags that stop gstack's compiled tools from reading a project's `.env`, so setup refuses it before writing anything. The same link appears on two warnings that do not stop setup: `warning: gstack is tested on Bun 1.4.2 (CI pin); found ` (1.3.3 up to 1.4.2 works but is untested) and `warning: could not read the Bun version (...)` (setup continued; check `bun --version`). **Fix.** ```bash bun upgrade ./setup ``` **Expected result.** `bun --version` prints 1.4.2 or newer and setup finishes with no Bun warning. ### `gstack auto-update: update held (bun-too-old: found Bun at ; gstack needs or newer); nothing was installed or changed. Fix now: ...` **Meaning.** Team-mode auto-update fetched a release whose setup needs a newer Bun than the one setup would run. It left your checkout and installed skills at the current revision and prints this line once per session start. (An older auto-updater running its first update cannot make this check.) **Fix.** `bun upgrade`; the next session start resumes the update. To update now: `bun upgrade && cd && git pull --ff-only && ./setup`. ### `gstack auto-update: setup did not finish (); installed skills may be out of date. gstack retries automatically. Fix now: cd && ./setup` **Meaning.** The pull worked but setup (or migrations) failed. gstack no longer says "just upgraded". It retries after 1 hour, then 6, then 24, and prints this line once per session start. A `(bun not found on PATH)` reason means install Bun first. **What is kept.** The previous install stays active. **Fix.** Run the printed command. ### `gstack auto-update: git pull did not finish (exit N: ...); gstack is not updating. Fix now: ...` **Fix.** `cd && git pull --ff-only && ./setup`. ### `gstack setup: refusing to register hooks that do not parse (Claude Code would block tool calls with them): :: ` **Meaning.** Claude Code runs gstack's hook shims through `/bin/sh`, and a hook that does not parse exits 2, which blocks the tool call it guards in every session. Setup parse-checks every hook it registers (the shim, and the TypeScript it runs with its local imports). It registers the hooks that parse, skips the ones listed, finishes the rest of the install, and exits non-zero. Claude Code runs hooks straight from `~/.claude/skills/gstack`, so a skipped hook that an earlier setup registered keeps running the broken file until it is fixed. This is a gstack bug, or a half-applied edit or merge in your checkout: report the printed `:`. **Fix.** ```bash git -C ~/.claude/skills/gstack status # half-applied edits or merge conflicts? git -C ~/.claude/skills/gstack checkout -- # or finish the merge cd ~/.claude/skills/gstack && ./setup ``` **Expected result.** Setup finishes with exit 0 and no refusal line. ### `gstack auto-update: update held (hook-does-not-parse: :: ); nothing was installed or changed, and your current hooks keep running. ...` **Meaning.** Team-mode auto-update fetched a release with a hook that does not parse. It checked the incoming revision before moving your checkout, so your checkout, installed skills and registered hooks stay at the current revision. gstack checks again at the next update check and installs the first release whose hooks parse. This is a gstack bug: report the printed `:`. **Fix.** Nothing to do locally. A manual `git pull` followed by `./setup` cannot be checked before the pull; setup then refuses the broken hook (see the entry above). ### `CSO unavailable: its native helper was not built (windows-msvc-compile)` **Meaning.** Visual Studio's compiler was found but the /cso native helper did not compile. setup prints the first compiler error. It used to say "install Visual Studio". **Fix.** Fix the printed compiler error, then re-run `./setup`. ### `Docker found at , but native Windows Docker transport is not supported yet; static assessment only.` / `docker.exe at is outside the trusted install locations (...)` **Meaning.** On Windows, /cso looks for `docker.exe` only under the install folders Windows reports for Program Files, Program Files (x86) and the Windows directory, by its real path, with no symlink or junction on the way. A user-writable directory is untrusted, because the Docker child carries registry credentials; that refusal cannot be overridden. Even a trusted `docker.exe` cannot run /cso's isolated containers yet: /cso admits only a local Unix Docker socket, and Docker Desktop on Windows speaks over a named pipe. /cso reports this and runs its static assessment only; no container or runtime check runs. **Fix.** For runtime checks, run /cso from Linux or macOS (WSL2 counts as Linux) with a local Docker socket. On Windows, static assessment is the supported mode; if the refusal named a user directory, install Docker Desktop under Program Files. ### `Snapshot manifest cap exceeded: ...` / `64 MiB source cap exceeded: ...` / `Symlink or special source file: ` **Meaning.** /cso copies the repository into a private snapshot before any audit starts, and refuses the whole run when the snapshot would be incomplete or too large. Each message names the cap that applied: - **Snapshot manifest cap (16 MiB).** One entry per tracked or nonignored untracked file; about 50,000 files fit. - **64 MiB source cap.** The full size of every such file outside dependency and VCS directories counts, including files over 1 MiB whose contents are withheld from the audit. - **Symlinks.** A tracked symlink anywhere in the repository stops the run. **Fix.** No setting raises these caps. Run /cso on a smaller checkout that holds the code you want audited and no symlinks. Add your file count or size to [#2993](https://github.com/garrytan/gstack/issues/2993). ### `removed the AskUserQuestion preference hook: it breaks Conductor's native AskUserQuestion (#2207). ...` **Meaning.** In Conductor, setup no longer installs the plan-tune question-preference hook and removes one it added before. The other plan-tune hooks stay. **Fix.** To keep it anyway: `gstack-config set plan_tune_hooks yes`, then `./setup`. ### `Error: browse_extension_id '' is not a Chrome extension ID ... Existing value left unchanged.` **Fix.** Copy the 32-letter ID (letters a to p) from `chrome://extensions`, then `gstack-config set browse_extension_id `. --- ## Browser ### `[browse] this install's browse CLI (, build ) and the gstack checkout's server bundle (, build ) are from different builds, so the server was not started. ...` **Meaning.** On Windows a host runtime root (`~/.codex/skills/gstack` and the other env-var hosts) holds a copy of browse with no `node_modules`, so its CLI starts the server bundle in the gstack checkout recorded in `.source-path`. The checkout was rebuilt (or updated) without refreshing this runtime root, so the two builds differ and browse refuses rather than run a mismatched server. **Fix.** Re-run setup from the checkout named in the message; it rebuilds and refreshes every runtime root: ```bash cd && ./setup ``` ### `[browse] chain: no flow to run (stdin was empty)` (or `stdin is a terminal`, `stdin could not be read (EAGAIN)`) **Meaning.** `browse chain` with no arguments runs the JSON flow piped to it. Nothing arrived, so it exits 1 before starting a browser. It used to exit 0 on Windows having run nothing. **Fix.** Pipe the flow, or pass it as an argument: ```bash echo '[["goto","https://example.com"],["text"]]' | browse chain browse chain 'goto https://example.com | text' ``` ### `Chromium at GSTACK_CHROMIUM_PATH= failed to launch: ...` **Meaning.** Headless browse and make-pdf now use `GSTACK_CHROMIUM_PATH` (NixOS, macOS 13). That binary could not start. **Fix.** Point `GSTACK_CHROMIUM_PATH` at a working Chromium, or `unset GSTACK_CHROMIUM_PATH` to use gstack's bundled one. ### `Headed Chromium profile is in use by Chromium PID (started by PID : ). ...` **Meaning.** Headed browse keeps one profile per project (`/.gstack/chromium-profile`). A live browser owns this one, and browse never kills it. `... is locked by PID on another host ()` means the lock came from another machine through a synced folder. **Fix.** Close that browser, or `export CHROMIUM_PROFILE=`. For the other-host case, stop it there, or remove `/SingletonLock` once you are sure nothing uses it. ### `[browse] Headed browse now keeps a profile per project ...` **Meaning.** First use copies your logins from `~/.gstack/chromium-profile` when no browser is using it; otherwise it starts fresh and prints how to import. `browse profiles` lists them, `browse profiles prune --days 30` removes idle ones. ### `Blocked: is a cloud metadata or link-local address` / `... resolves to a cloud metadata or link-local address` **Meaning.** Browse refuses 169.254.0.0/16 (including container credential endpoints), 100.100.100.200, fe80::/10 and fc00::/7 in every numeric spelling, including after a redirect or a page-driven navigation. The tab is reset to `about:blank` and the command fails. This cannot be overridden. ### `forbidden origin` from the sidebar terminal **Meaning.** The terminal accepts only gstack's extension. A self-built or forked extension has a different ID. `BROWSE_EXTENSION_ID` is no longer read. **Fix.** `gstack-config set browse_extension_id `. ### `hint: "" looks like a CSS selector` **Meaning.** Bare `browse type ` types into the focused element. The first word looked like a selector, so it was typed as text. **Fix.** `browse type --selector '' ` (put text that starts with `--` after `--`). ### `server-node.mjs not found. Run bun run build` (Windows) **Fix.** `cd && bun run build`. ### `[freeze] Hook helpers out of date (partial upgrade?)` **Meaning.** `check-freeze.sh` and `hook-extract.sh` come from different gstack versions. **Fix.** `cd && ./setup` (or `/unfreeze`). ### `[browse] Could not record the Chromium PID (CDP SystemInfo.getProcessInfo: ); browse stop cannot reap a surviving Chromium.` **Meaning.** For a headless Chromium it launched, browse reads the browser's process id over CDP so `browse stop` can kill a browser that outlives the server. That read failed or took longer than 2 seconds, so this session's browser is not recorded. Browsers gstack did not launch are never recorded or killed. **Fix.** After `browse stop`, check for a leftover browser with `ps aux | grep -i chrom` and end it with `kill `. --- ## Memory and gbrain Every message below is also in [docs/gbrain-sync-errors.md](gbrain-sync-errors.md) with more detail, along with the per-page ingest messages (`FAILED`, `re-queued`, `quarantined`, reconcile progress, kept-on-this-machine notices). ### `database host unreachable ( ); your gbrain config is unchanged. Fix: check network or VPN, then re-run /sync-gbrain.` **Meaning.** A DNS or connection error reaching your gbrain database. It used to look like a broken config, and setup stripped brain features while offline. **Fix.** Restore the network or VPN, then `/sync-gbrain`. ### `[memory-ingest] ERR: gbrain reported N failure(s) it did not attribute to a staged page ... Refusing to advance state.` **What is kept.** Nothing is marked saved, so nothing is lost. After three such runs gstack bisects the batch and quarantines the page that breaks it. **Fix.** Re-run `/sync-gbrain`. ### `[memory-ingest] reconcile: not run (...)` **Meaning.** After upgrading, gstack re-checks transcripts it had marked saved and re-imports the ones missing from your brain. That check needs a local gbrain. **Fix.** `/setup-gbrain`, then `gstack-memory-ingest --reconcile` (`--dry-run` to preview). ### `[memory-ingest] ERR: another memory ingest (pid ) is writing ; not run.` **Fix.** Wait for it to finish, then re-run `/sync-gbrain`. ### `[memory-ingest] ERR: could not save ingest state : .` **Fix.** Fix permissions or free disk space under your gstack state root, then `/sync-gbrain`. ### `curated artifacts pushed to git, but gbrain source has 0 indexed pages. Fix: gbrain sync --source , then re-run /sync-gbrain` **Fix.** `gbrain sync --source `, then `/sync-gbrain`. ### `dream: skipped — the installed gbrain cannot run only the resolve_symbol_edges phase, and the full dream cycle costs about 35 minutes ...` **Meaning.** `/sync-gbrain --dream` (and `--full`'s auto-build) now runs only the call-graph phase. Your gbrain cannot scope it, so nothing ran. **Fix.** `gstack-gbrain-install`, then `/sync-gbrain --dream`. Or run the full cycle yourself: `gbrain dream --source `. ### `call graph for : unknown: installed gbrain does not expose cycle_freshness` **Fix.** `gstack-gbrain-install`. ### `[gbrain-sync] gbrain source : path unavailable (); gstack skips it.` **Meaning.** A deleted worktree's code source. gstack never removes it on its own. **Fix.** Preview, then remove the ones gstack can prove belonged to this machine: ```bash gstack-gbrain-sync --prune-gone-worktrees --dry-run gstack-gbrain-sync --prune-gone-worktrees ``` --- ## Eval reports and pass-rates These come from the paid eval census reports and `bun run eval:pass-rates`. The walkthrough for a red census is [docs/evals/census-red.md](evals/census-red.md). ### `eval:pass-rates: unknown flag ` / `eval:pass-rates: unknown case (not an E2E, judge or paid test file id)` **Meaning.** The flag or case id is not recognized. The command exits 2 before fetching anything. **Fix.** `bun run eval:pass-rates --help` lists the flags. `--case` takes a registry id from `test/helpers/touchfiles-data.ts` or a paid test file path. ### `unknown failure_cause "" (written by a newer gstack; update this checkout to read it)` **Meaning.** A trial record names a failure cause this checkout does not know. A newer gstack wrote it. **Fix.** `git pull` (or rebase your branch onto `main`), then rerun the command. ### `--case : registered by , ; it needs exactly one` **Meaning.** More than one paid test file registers the case, so `test-paid-shards.ts --case` cannot pick one file. **Fix.** Run the red shard's file directly; the report's `after a repair:` line prints it: `EVALS=1 EVALS_TIER= bun test `. ### `evidence: too large to fetch ( MB)` **Meaning.** The slice artifact holding this red's transcript is over 64 MB, so `eval:pass-rates --run` does not download it. **Fix.** Download it from the run page, or `gh api repos///actions/artifacts//zip > slice.zip`. ### `[headroom] session : max of () over sample(s), above the 85% cap. Cut work in the skill or fixture; budgets are never raised ...` **Meaning.** `eval:pass-rates --gate` (the weekly report) found a case whose slowest session used more than 85% of the timeout it armed. It is one timeout away from a red. **Fix.** Cut work in the skill or fixture. Budgets are never raised. Check the case with `bun run eval:pass-rates --headroom --case `. ### `cost unknown (no billing captured for any of trial(s))` / `cost $ known + of trial(s) cost unknown` **Meaning.** PTY and Codex sessions record no billing, so their cost is unknown. Only the known sum is shown. **Fix.** None needed; this is informational. ### `· cause provider_stall: no stream event for s ...` **Meaning.** The session streamed partial messages, then received no event of any kind for at least 120 seconds while a model request was in flight and no tool, permission prompt, hook or subagent was outstanding. Under EVAL_POLICY v1 it is still a failed trial. **Fix.** Inspect it with `bun run eval:pass-rates --run --case `. A paid rerun happens only after a repair. ### Overlay record with `taskCorrect: false` and `answerError` (contract v4) **Meaning.** The trial completed with a wrong answer. Under overlay contract v4 that is a valid measurement in the comparison; only an overlay-ON wrong answer fails the case. **Fix.** Read `answerError` in the trial JSON. No rerun is needed. ### `[detector-corpora] stale entries (refresh from a current census when one fails)` **Meaning.** Some replay-corpus entries came from an older input series of their case. This is informational; the corpus still replays them. **Fix.** When that case goes red in a census, add the new capture to its corpus and check it with `bun test test/detector-corpus-.test.ts`. ### `recommendation substance median < 4 over samples [,,] (boilerplate/weak)` **Meaning.** The auq-matrix case scores each captured question's recommendation with a 3-sample judge panel; fewer than 2 of the 3 samples reached 4, so their median was below 4. **Fix.** Read the logged samples and the captured question. A fix goes in the skill text, followed by one diagnostic run of auq-matrix. --- ## Push guard (redaction) ### `db.url_with_password` HIGH on a default `postgres`/`postgres` login whose host is a service name like `@db` (pre-push BLOCKED) **Meaning.** The default `postgres:postgres` pair is exempt only on `localhost` or `127.0.0.1`. A service name like `@db` says nothing about where the URL ends up deployed, so it still blocks. **Fix.** Use `postgres://postgres:postgres@localhost:5432/...` in local and CI config, or read the URL from an env var. If the credential is real, rotate it. Bypass once: `GSTACK_REDACT_PREPUSH=skip git push`. ### `pii.ip_public` MEDIUM on a four-part version number **Meaning.** The line does not declare the value as a version. **Fix.** Write it as `version: 1.2.3.4`, `"version": "..."` or `v1.2.3.4`. MEDIUM findings do not block pushes. ### `MEDIUM pii.email :` from the pre-push hook **Meaning.** The pushed lines add an email address that is not yours and not already in the destination's commit metadata. The hook does not report your own address (`git config user.email`), addresses listed in `gstack.redact.allowEmail`, or author and committer addresses (mailmapped too) from the history of the pushed commits, of the remote tip being replaced, and, for a new branch pushed to a configured remote, of that remote's tracking refs. An address only another remote knows still reports. If the hook also printed `existing-email suppression was limited for this push`, it could not read the history in 5 seconds, so known authors may be listed too. **Fix.** Remove the address, or allow it for this repo: `git config --add gstack.redact.allowEmail
`. If it is your own address, check `git config user.email`. The allowlist only covers `pii.email`; it never lets a HIGH finding through.