Skip to main content
If gc start fails after install, use the gc start failure walkthrough to match the final FATAL: line to the likely cause and resolution.

Run the Built-in Doctor

gc doctor checks your city for structural, config, dependency, and runtime issues. It is always the best first step:
gc doctor
gc doctor --verbose   # extra detail
gc doctor --fix       # attempt automatic repairs

Add City-Local Doctor Checks

Use [[doctor.check]] in city.toml for a workspace-specific health check that does not need to be packaged as a reusable pack doctor. Provide the bare check name; gc doctor adds the local: prefix in output.
[doctor]

[[doctor.check]]
name = "gopath-symlink"
description = "Verify the GOPATH symlink used by local build scripts"
script = "scripts/check-gopath.sh"
fix = "scripts/fix-gopath.sh"
The script and optional fix paths are relative to the city root. Absolute paths and paths that escape the city directory are rejected and reported as named StatusError check results. Local checks reuse the same script protocol as pack doctor checks:
Exit codeResult
0OK
1Warning
2 or higherError
The first stdout line becomes the check message. Additional stdout lines are shown by gc doctor --verbose.

”command not found” After Install

If gc is installed but your shell cannot find it, the binary is not on your PATH. Homebrew puts binaries in a directory that is usually already on your PATH. Run brew --prefix to confirm, then check that $(brew --prefix)/bin appears in your PATH. Direct download requires you to move or symlink the binary into a directory on your PATH:
install -m 755 gc ~/.local/bin/gc   # or /usr/local/bin/gc
Then verify:
which gc
gc version
If you use a non-standard shell (fish, nushell), check that shell’s PATH configuration rather than ~/.bashrc or ~/.zshrc.

Oh My Zsh Git Plugin Hides gc

Oh My Zsh’s git plugin defines gc as an alias for git commit --verbose. When that alias is active, commands like gc version, gc init, or gc start run git instead of the Gas City binary. Temporary workaround:
command gc version
command gc init ~/my-city
command bypasses shell aliases for that invocation. Persistent fix in ~/.zshrc:
source "$ZSH/oh-my-zsh.sh"
unalias gc 2>/dev/null
The unalias line must come after Oh My Zsh loads. If it appears before source "$ZSH/oh-my-zsh.sh", the git plugin recreates the alias later. Oh My Zsh also loads files in $ZSH_CUSTOM after built-in plugins, so this is a good alternative:
mkdir -p ~/.oh-my-zsh/custom
printf '%s\n' 'unalias gc 2>/dev/null' > ~/.oh-my-zsh/custom/gascity.zsh
If you do not use Oh My Zsh git aliases, you can also remove git from the plugins=(...) list.

Missing Prerequisites

gc init and gc start check for required tools and report any that are missing. You can also run gc doctor inside an existing city for a fuller check.

Always required

ToolmacOSDebian / Ubuntu
tmuxbrew install tmuxapt install tmux
gitbrew install gitapt install git
jqbrew install jqapt install jq
pgrepincludedapt install procps
lsofincludedapt install lsof

Required for the default beads provider (bd)

ToolMin versionmacOSLinux
dolt2.1.0 or newerbrew install doltreleases
bd1.0.0releasesreleases
flockbrew install flockapt install util-linux

Optional for GitHub gates

ToolmacOSLinux
ghbrew install ghcli.github.com
Gas City can run without gh. Maintenance skips GitHub gate checks when the GitHub CLI is not installed. If you do not want to install dolt, bd, and flock, switch to the file-based store:
export GC_BEADS=file
Or add this to your city.toml:
[beads]
provider = "file"
The file provider is fine for trying Gas City locally. The bd provider adds durable versioned storage and is recommended for real work.

Dolt Version Too Old

Gas City requires a final Dolt 2.1.0 or newer. Older and pre-release builds are below the managed bd/Dolt compatibility floor; releases before 1.86.2 can also miss the upstream GC/writer deadlock fix in dolthub/dolt commit ccf7bde206, which can hang dolt_backup sync under heavy write load. Check your version:
dolt version
Upgrade via Homebrew (brew upgrade dolt) or download a newer release from dolthub/dolt/releases.

bd Version Too Old

Gas City requires bd 1.0.0 or newer. The bd-backed store relies on wisps support, including bd create --ephemeral and bd query ephemeral=true, so older binaries can fail order-tracking and wisp cleanup paths. Check your version:
bd version
Upgrade via Homebrew (brew upgrade beads) or download a newer release from gastownhall/beads/releases.

Native Store Falls Back Because Hooks Are Installed

Native bd store selection intentionally falls back to the subprocess-backed store when executable .beads/hooks/on_create, .beads/hooks/on_update, or .beads/hooks/on_close scripts are present. Those hooks historically emitted bead events for external bd writes; the native in-process store does not run shell hooks. For controller-managed Gas City deployments, confirm that the controller is wrapping stores with CachingStore and emitting bead.created, bead.updated, bead.closed, and bead.deleted events to the event bus. After that migration is verified, remove the executable hook scripts from the city or rig .beads/hooks/ directory to allow native store adoption. Keep GC_BEADS_FORCE_FALLBACK=1 set when a deployment still depends on those hook scripts directly.

flock Not Found (macOS)

macOS does not ship flock. Install it via Homebrew:
brew install flock
Alternatively, switch to the file-based beads provider (see above) to skip the flock requirement entirely.

Cursor MCP Tools Still Prompt or Appear Unavailable

The built-in cursor provider starts cursor-agent with -f and leaves Cursor’s MCP approval prompt enabled by default. This avoids silently approving user or global MCP servers that Cursor can also see through ~/.cursor/mcp.json. For unattended Cursor pool workers, opt in only after confirming that every workspace and user/global MCP server visible to Cursor is trusted. The --approve-mcps flag approves every visible server, including servers projected from Gas City’s catalog into .cursor/mcp.json and servers from ~/.cursor/mcp.json.
[providers.cursor.option_defaults]
mcp_approval = "approve"
If you override Cursor args directly, the override replaces the built-in args. Include -f yourself and add --approve-mcps only for the same explicit trust decision. Agent-level args overrides behave the same way. Existing Cursor sessions keep the command fingerprint they were created with. The supervisor reconciler restarts sessions automatically after the fingerprint changes. Drain the pool first when you need a controlled handoff rather than waiting for the next automatic restart.

gc version Prints Unexpected Output

If gc version prints git progress lines (Enumerating objects...) instead of a clean version string, upgrade to Gas City v0.13.4 or later. This was a bug where remote pack fetches wrote git sideband output to the terminal, fixed in PR #141.

Provider Credentials Dropped When the Supervisor Starts

Symptom: agents authenticate fine when you launch a city from your normal interactive shell, but fail to authenticate (or silently fall back to a different provider) when the city is started by the supervisor at login or after a reboot. Cause: the supervisor service file (launchd plist / systemd unit) captures provider credentials by snapshotting the environment of the shell that ran gc start (or gc supervisor install). A credential that is only present in an interactive shell — for example sourced from an rc file that the login service manager never reads — is not in that snapshot, so it never reaches the supervised process. Fix: put the durable credentials in a machine-local secrets file at ${GC_HOME}/secrets.env (defaults to ~/.gc/secrets.env). On every service file regeneration, gc merges this file into the supervisor environment, so the value survives a reboot regardless of which shell ran gc start.
# ~/.gc/secrets.env  (chmod 600)
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
The file uses dotenv syntax: KEY=VALUE per line, # comments, blank lines, an optional export prefix, and optional surrounding quotes. Only keys that are already eligible for the supervisor environment are merged — provider credentials (recognized by their standard prefixes such as ANTHROPIC_, OPENAI_, GEMINI_) plus any keys you opt in via GC_SUPERVISOR_ENV; any other key in the file is ignored. A value exported in the calling shell still takes precedence over the file, and GC_SUPERVISOR_OMIT_PROVIDER_CREDS=1 suppresses provider credentials from both sources. Apply the change by regenerating the service file:
gc service restart     # restarts the launchd/systemd service

JSONL Archive Push Failures

The maintenance pack runs jsonl-export every 15 minutes to dump each bead database to a text-diffable JSONL snapshot inside a local git repository (the “JSONL archive”). The archive serves as a disaster-recovery backup: if the live Dolt server loses data, the last-known-good bead graph can be reconstructed from the archive’s commit history.

Local-only vs push mode

The archive operates in one of two modes, detected from the state of its git remotes on every run:
  • Local-only (default). No origin remote is configured. Commits are created and retained on the host but never leave the machine. This mode is safe to run indefinitely; its only limitation is that the archive is not backed up off-box, so a disk failure on this host loses the archive alongside the live Dolt data.
  • Push. An origin remote is configured. Each run rebases onto origin/main and pushes new commits so the archive survives a host loss.
On each run jsonl-export logs the active mode to stderr on transitions (e.g. after you add or remove origin) and re-logs it at least weekly so that an operator reading the log file can always find the current mode.

Enabling off-box backup

Pick a repository that only this host will push to (the archive contains bead content and should not be shared across cities). Then:
# Create a private repo on your git host (example: GitHub via gh)
gh repo create my-city-jsonl-archive --private

# Point the archive at it (run from anywhere inside your city)
ARCHIVE="$(gc status --json | jq -r '.city_path')/.gc/runtime/packs/maintenance/jsonl-archive"
git -C "$ARCHIVE" remote add origin [email protected]:<you>/my-city-jsonl-archive.git

# Seed the remote with the existing local history
git -C "$ARCHIVE" push -u origin main
On the next 15-minute tick, jsonl-export detects the new origin, logs archive running in push mode, and resumes pushing every run.

Switching back to local-only

Remove the remote:
git -C "$ARCHIVE" remote remove origin
Re-detection is automatic on the next run — no state-file edits are required. The next log line will read archive running in local-only mode. If push mode had accumulated failures before the remote was removed, local-only detection clears that stale failure counter while retaining pending_archive_push so deferred commits are still pushed if origin returns.

Reading a JSONL push failed [HIGH] escalation

When push mode is active and git push fails GC_JSONL_MAX_PUSH_FAILURES times in a row (default: 3), the mayor’s inbox receives an ESCALATION: JSONL push failed [HIGH] message with a body shaped like:
Order: mol-dog-jsonl
Archive: /path/to/archive
Consecutive failures: 3 (threshold: 3)

Last git push stderr:
<last ~20 lines of captured stderr from fetch / rebase / push>

Remediation:
- Check remote: git -C <archive> remote -v
- Verify remote is reachable and credentials are valid
- Temporarily suppress: export GC_JSONL_MAX_PUSH_FAILURES=99
- See docs/getting-started/troubleshooting.md#jsonl-archive-push-failures
Transient ref-update races are retried before the escalation counter is incremented. By default, each retry sleeps for a random delay from 1 to 5 seconds. Set GC_JSONL_PUSH_RETRY_DELAY_MIN to change the lower bound and GC_JSONL_PUSH_RETRY_DELAY_SPAN to change the random span added above that minimum. The exporter sends one HIGH escalation for a still-unresolved push failure. It continues recording consecutive_push_failures and pending_archive_push in state, but does not mail the same failure on every tick. A successful push or a switch back to local-only mode clears the escalation marker. Common root causes, in rough order of frequency:
  • Credentials rotated or expired. SSH key removed from the remote host, HTTPS token expired. The captured stderr usually reads Permission denied (publickey) or remote: Invalid username or password.
  • Remote URL typo or deleted repo. stderr reads does not appear to be a git repository or repository not found.
  • Network partition. stderr reads Could not resolve host or a connection-timeout message. If the host is also firewalled from the rest of the internet, this will recover once connectivity returns.
  • Diverged history. Very unusual — the archive rebases onto origin/main automatically — but if the remote was force-pushed from another host, rebase may fail with a conflict. Inspecting the archive and resolving manually is the only option.
If the underlying problem cannot be fixed immediately (e.g., the remote host is down for scheduled maintenance), set GC_JSONL_MAX_PUSH_FAILURES=99 in the maintenance pack’s environment and restart the city with gc restart. That bumps the escalation threshold from 3 to 99, which at the current 15-minute tick rate is ~24 hours of silence.

WSL (Windows Subsystem for Linux)

Gas City works under WSL 2 with a standard Ubuntu or Debian distribution. Install prerequisites using the Linux column in the tables above. tmux requires a working terminal — use Windows Terminal or another WSL-aware terminal emulator.

Build From Source Fails

Building from source requires make and Go 1.25 or newer:
make --version
go version
If make is missing, install it (apt install make on Debian/Ubuntu, or xcode-select --install on macOS). If your Go version is too old, update it from go.dev/dl or via your package manager. Then:
make build
./bin/gc version
See CONTRIBUTING.md for the full contributor setup.

Slung Beads Not Reaching Agents (managed-city mode)

If gc sling accepts work but agents don’t process it — especially if your supervisor log shows rigStores=0 or assignedWorkBeads=0, or your bd dolt set port edits keep reverting at the next gc start — you’re likely looking at a rig whose Dolt view has drifted from the managed city Dolt. Do not edit .beads/dolt-server.port or bd dolt set port directly; both self-revert. See the Managed-city Dolt endpoints runbook for the mental model, the forbidden edits, the sanctioned escape hatches (gc rig set-endpoint --inherit/--self --force/--external), and an end-to-end recovery recipe.

Still Stuck?

Open an issue at gastownhall/gascity/issues with the output of gc doctor --verbose and your OS/architecture.
Last modified on June 3, 2026