gastownhall/beads · error

timeout waiting for proxy to become ready on %s

Error message

timeout waiting for proxy to become ready on %s

What it means

Returned by spawnAndHandoff when the caller's deadline expires while waiting for the spawned proxy to become ready and the child was killed cleanly. It is the normal (non-hard-timeout) startup-timeout outcome and reports which port the proxy was expected on.

Source

Thrown at internal/storage/dbproxy/proxy/endpoint.go:404

					opts.Port, opts.LogFilePath, childErr,
				)
			}
			return Endpoint{}, fmt.Errorf(
				"proxy child exited before publishing its OS-assigned port (see %s): %w",
				opts.LogFilePath, childErr,
			)
		case <-hard.C:
			if err := killSpawnedChild(child); err != nil {
				return Endpoint{}, fmt.Errorf("hard timeout waiting for proxy on %s; safe child kill failed: %w", describeSpawnPort(opts.Port), err)
			}
			return Endpoint{}, fmt.Errorf("hard timeout (%s) waiting for proxy on %s", spawnReadyHardTimeout, describeSpawnPort(opts.Port))
		case <-poll.C:
		}
		if time.Now().After(deadline) {
			if err := killSpawnedChild(child); err != nil {
				return Endpoint{}, fmt.Errorf("timeout waiting for proxy on %s; safe child kill failed: %w", describeSpawnPort(opts.Port), err)
			}
			return Endpoint{}, fmt.Errorf("timeout waiting for proxy to become ready on %s", describeSpawnPort(opts.Port))
		}
	}
}

// describeSpawnPort renders a requested spawn port for wait/timeout
// messages: 0 is the default OS-assigned path, not a literal "port 0".
func describeSpawnPort(port int) string {
	if port == 0 {
		return "its OS-assigned port"
	}
	return fmt.Sprintf("port %d", port)
}

type spawnedProxyChild struct {
	cmd    *exec.Cmd
	done   <-chan error
	handle *procid.Handle
	marker spawnMarker

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the child log (opts.LogFilePath) and discovery records under the workspace root for partial startup progress.
  2. Increase the deadline passed into the open call to reflect actual cold-start cost.
  3. Retry — after cleanup the next attempt often succeeds faster (warm caches).
  4. Ensure no other process is interfering (stale locks, antivirus, resource limits) and that the workspace is on local disk where possible.

Example fix

// before: 5s deadline for a 20s cold start
ctxDeadline := time.Now().Add(5 * time.Second)
// after: derive deadline from observed cold-start time
coldStart := 30 * time.Second
ctxDeadline := time.Now().Add(coldStart)
Defensive patterns

Strategy: retry

Validate before calling

deadline := time.Now().Add(coldStartBudget) // size budget to your environment
if time.Until(deadline) < minReasonableSpawnWindow {
    return errors.New("deadline too short for proxy cold start")
}

Try / catch

ep, err := GetCreateDatabaseProxyServerEndpoint(rootDir, opts)
if err != nil && strings.Contains(err.Error(), "timeout waiting for proxy to become ready") {
    log.Printf("spawn timed out; child log at %s", opts.LogFilePath)
    time.Sleep(time.Second)
    ep, err = GetCreateDatabaseProxyServerEndpoint(rootDir, opts)
}

Prevention

When it happens

Trigger: The poll loop in spawnAndHandoff observes time.Now().After(deadline) before the child publishes a dialable endpoint: backend initialization is slow, the child hangs, or the readiness record is never written.

Common situations: Very large or cold Dolt database; slow/NFS-backed workspace; overloaded machine; caller set an unrealistically short deadline; a wedged dolt subprocess inside the child blocking readiness.

Understand the failure class

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/8a9cc9f6491f5e5b. Report an issue: GitHub.