Concurrent tilt instances can lose each other's apiserver config entries ("No tilt apiserver found" while running)
Expected Behavior
When several tilt instances run concurrently, each one's entry in the shared API server config (~/.tilt-dev/config) should survive until that instance shuts down, so tilt get/tilt api can always find a running server.
Current Behavior
Registration and removal of the tilt-<port> config entry are non-atomic read-modify-write operations, so concurrent Tilt processes can lose each other's entries.
In internal/hud/server/controller.go, both addToAPIServerConfig and removeFromAPIServerConfig do:
- take a read lock, read the whole config, release the lock
- mutate an in-memory copy
- later take a write lock and write the whole copy back
The write lock serializes writers, but nothing protects the full transaction. Two processes can read the same base snapshot and then write divergent snapshots; the later write silently discards the earlier process's registration (or its removal).
The visible symptom is a CLI command failing with:
No tilt apiserver found: tilt-<port>while that Tilt instance is still running and serving normally — its UI is up, its API server is listening, only the config entry is gone. internal/cli/client/config.go emits that error purely on config-lookup failure, so it does not imply the server is down.
The same race can strand a stale entry: if A's removal is overwritten by B's write, A's entry outlives A's process.
This is distinct from #4814 (stale config.lock file) — the lock file works as intended here; the problem is that the lock isn't held across the whole read-modify-write.
Steps to Reproduce
- Start several Tilt instances concurrently on different ports (e.g. 8–10 of them,
tilt up --port <n>), against one shared config file. - While they're starting, run
tilt get session --port <n>against them. - Shut them all down at once.
- Some instances report
No tilt apiserver found: tilt-<port>while running, and/or entries remain in the config after every process has exited.
The race is timing-dependent, so it may take a few rounds. A deterministic in-process reproduction is included as a Go test in the linked PR: it fails on master (an entry survives after all removals) and passes with the fix.
Context
About Your Use Case
We hit this in an automated dev-environment harness that launches Tilt programmatically and polls tilt get portforward --port <selected-port> to decide when the environment is ready. Tilt itself was healthy and serving, but the readiness poll failed with No tilt apiserver found: tilt-<port>, so the harness tore down a working environment. Other Tilt processes were running on the machine at the time.
Anyone running multiple Tilt instances side by side (several projects at once, or CI-style automation) can hit this.
I have a fix ready and will open a PR shortly.
Source: tilt-dev/tilt