Troubleshooting
Common issues organized by symptom. Start with gtl doctor, which catches many of these automatically. gtl doctor --fix applies the remediations the doctor would otherwise just print as hints.
HTTPS router and certificates
Connection refused on .prt.dev URLs
The router isn’t running, the CA certificate expired, or port forwarding (443 → 3001) is missing.
$ gtl serve status # check if the router is alive
$ gtl serve install # idempotent re-run
HTTPS worked before but suddenly stopped
The local CA certificate expires after one year. Treeline auto-renews when within 7 days of expiry, but if the router hasn’t run in a while, the cert may have lapsed.
$ gtl serve install # regenerates CA if expired
Router version mismatch after upgrading the CLI
Since v0.40.1, brew upgrade git-treeline auto-bounces the router via brew’s post_install. If the warning still appears (older brew formula, manual install, or a non-brew install), bounce it explicitly. gtl serve restart is the fast path — no sudo, no plist rewrite.
$ gtl serve restart # fast launchctl bounce
# Or, if the plist itself needs to be regenerated:
$ gtl serve install
Branch URLs broke after a reboot
macOS’s built-in pf service loads pf.conf at boot but does not enable pf, so the 443→3001 redirect silently disappeared after every restart. v0.40.1 ships a LaunchDaemon that re-enables pf at boot — existing installs need to re-run install once to pick it up. gtl doctor warns when the daemon is missing.
$ gtl serve install # installs the boot-time pf reloader
# Just need to reload pf right now without a full reinstall?
$ gtl serve reload-pf
Safari can’t reach branch URLs
Safari does not resolve *.localhost to loopback. Switch to prt.dev (real DNS, works in all browsers) or sync hosts entries.
$ gtl config set router.domain prt.dev
$ gtl serve install
# Or keep localhost and sync /etc/hosts:
$ gtl serve hosts sync
Sessions, CSRF, and login
Login fails / CSRF token mismatch through the router
Before v0.35.4, the proxy did not set X-Forwarded-Proto. Rails compared Origin: https:// against request.base_url which returned http://, causing origin mismatches. Upgrade to v0.35.4+.
$ brew upgrade git-treeline
“Blocked host” error in Rails
Rails rejects requests whose Host header doesn’t match config.hosts. Whitelist the router domain.
env: ROUTER_DOMAIN: "{router_domain}"
config.hosts << ".#{ENV.fetch('ROUTER_DOMAIN', 'prt.dev')}" Vite / Django allowed hosts equivalent
Vite: add server.allowedHosts: [".prt.dev"] to vite.config.js. Django: add .prt.dev to ALLOWED_HOSTS. gtl tunnel prints framework-specific hints when it detects a misconfiguration.
Stale config and environment
Changed .treeline.yml but the env file has old values
env: changes sync automatically on gtl start and gtl restart. If you manage the server yourself, sync manually.
$ gtl env sync # re-writes env file + editor settings
Changed commands.start but the server runs the old command
The supervisor caches the start command at launch. Kill it and start fresh.
# In the terminal running gtl start:
Ctrl+C # kills the supervisor
$ gtl start # picks up the new command
gtl link didn’t update the running app
Since v0.33.0, gtl link automatically regenerates the env file and restarts the supervised server. On older versions, run gtl env sync and restart manually.
Ports and allocation
“Port already in use”
Another process grabbed the port since the last allocation. Treeline detects this at setup time (v0.28.0+) and re-allocates. For existing worktrees, refresh.
$ gtl refresh # re-allocates all worktrees with current config
Browser says ERR_UNSAFE_PORT
Browsers refuse connections to ports on the WHATWG blocked list (e.g. 6000, 6665–6669). Since v0.30.0, the allocator skips these. Run gtl refresh to re-allocate.
App starts but on the wrong port
Some frameworks (Vite, Next.js, Django) don’t read PORT for their listen port. Wire it into the start command. gtl doctor detects this.
commands: start: next dev --port {port}
commands: start: npx vite --port {port}
Upgrades and config migration
Deprecation warnings about old config keys
Treeline auto-migrates renamed keys on first load: setup_commands → commands.setup, start_command → commands.start, default_branch → merge_target, ports_needed → port_count. The old key is rewritten automatically.
Typo in .treeline.yml or config.json
Since v0.30.0, gtl doctor and gtl init detect unrecognized keys and suggest corrections with “did you mean?” based on edit distance.
createdb: ERROR: syntax error at or near "-"
A dash in project: or database.template: flows through {project}/{template} substitution and lands in an identifier Postgres rejects. v0.40.2 validates these at config load and prints a gtl rename <suggested> hint. gtl rename updates the registry, drops the old DBs, and re-runs setup so worktrees come back up under the sanitized name.
$ gtl rename fitter_app # sanitized form of fitter-app
Existing ports conflict with new defaults after upgrade
v0.31.0 changed port.base from 3000 to 3002 and port.increment from 10 to 2. Existing allocations keep their ports. Run gtl refresh to re-allocate with the new defaults.
Registry references worktrees that no longer exist
When directories get deleted outside of gtl release, the registry is left holding orphan entries. v0.40.0 adds an audit and safe automatic repair that backs up the registry before pruning.
$ gtl registry validate # dry-run audit
$ gtl registry repair # prune orphans (writes a backup first)
Many worktrees lost their allocation after a major upgrade
gtl reallocate bulk re-runs setup. Defaults to dry-run; pass --force to apply. Useful after registry repair or a Conductor-wide rebuild.
$ gtl reallocate --all-registry
$ gtl reallocate --from ~/conductor/workspaces # scan a Conductor-style layout
General
508 Loop Detected from the router
The router counts proxy hops via X-Gtl-Hops and returns 508 after 5. This usually means a circular route. Check your {resolve:*} links for cycles.
Long branch name can’t be routed
DNS labels are limited to 63 characters. Since v0.30.0, Treeline auto-truncates long route keys with a deterministic hash suffix. On older versions, shorten the branch name or upgrade.
Stale supervisor socket
If a previous supervisor crashed without cleanup, the socket file may linger. Since v0.30.0, gtl start detects this and reports the conflict. Remove the stale socket or kill the orphan process.