diff --git a/docs/content/guides/troubleshooting.mdx b/docs/content/guides/troubleshooting.mdx index 7e1168e..73fa1d9 100644 --- a/docs/content/guides/troubleshooting.mdx +++ b/docs/content/guides/troubleshooting.mdx @@ -78,3 +78,9 @@ Context holding ports as app-main, and resolving app now ## The daemon is serving an old build A running daemon keeps serving the build it started as, and it only complains about a mismatch when the wire protocol changed. After upgrading grove, `grove restart`. Detached ports are taken again on the far side; a lease held by a running command is not, so that command has to be run again, and restart tells you which routes that cost. + +## Two projects install different groves + +One daemon serves the machine, so when the wire protocol changes between versions only one of those groves can talk to it. Whichever project restarts last owns the daemon, and every project on the other version gets a protocol mismatch until it upgrades too. Restarting from the stranded project only trades which one works. + +The fix is to put every project on the same grove, not to keep restarting. [`grove doctor`](/reference/cli/doctor) reads the version a project installs under `Project grove`, which is where a workspace resolving more than one shows up. diff --git a/internal/daemon/client.go b/internal/daemon/client.go index ea579d8..9b98460 100644 --- a/internal/daemon/client.go +++ b/internal/daemon/client.go @@ -241,5 +241,5 @@ func (e *VersionError) Error() string { // from inside a project that installed grove rather than from a shell that // has it on PATH. grove := shell.Invocation() - return fmt.Sprintf("the running daemon speaks control protocol v%d and this grove speaks v%d; restart it with '%s restart', then '%s hold' in each project, since a restart drops every detached port", e.Daemon, e.CLI, grove, grove) + return fmt.Sprintf("the running daemon speaks control protocol v%d and this grove speaks v%d; restart it with '%s restart' to hand it to this build, which says what that cost. One daemon serves the machine, so a project installing the other version cannot reach it until that project upgrades too.", e.Daemon, e.CLI, grove) } diff --git a/internal/daemon/server_test.go b/internal/daemon/server_test.go index 9b01e1a..9058e10 100644 --- a/internal/daemon/server_test.go +++ b/internal/daemon/server_test.go @@ -385,7 +385,10 @@ func TestClientNamesAnOlderDaemon(t *testing.T) { if !errors.As(err, &mismatch) { t.Fatalf("err = %v, want VersionError", err) } - for _, want := range []string{"grove restart", "grove hold"} { + // The prose as well as the command: a restart alone settles nothing while + // two projects install different versions, and that is the half worth + // keeping asserted. + for _, want := range []string{"grove restart", "One daemon serves the machine"} { if !strings.Contains(err.Error(), want) { t.Errorf("error does not name %q, so it says a problem without a way out: %v", want, err) } @@ -924,7 +927,7 @@ func TestTheErrorsNameHowGroveWasReached(t *testing.T) { grove := shell.Invocation() mismatch := (&daemon.VersionError{Daemon: 3, CLI: 4}).Error() - if !strings.Contains(mismatch, "'"+grove+" restart'") || !strings.Contains(mismatch, "'"+grove+" hold'") { + if !strings.Contains(mismatch, "'"+grove+" restart'") { t.Errorf("version error does not name the caller's grove: %q", mismatch) }