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.

.treeline.yml
env:
  ROUTER_DOMAIN: "{router_domain}"
config/environments/development.rb
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.

.treeline.yml — Next.js
commands:
  start: next dev --port {port}
.treeline.yml — Vite
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_commandscommands.setup, start_commandcommands.start, default_branchmerge_target, ports_neededport_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.