JuliusBrussee/caveman · error

native runtime: socket already active

Error message

native runtime: socket already active

What it means

The unix-socket Serve function probes an existing socket path with a 50ms dial before listening. If the dial succeeds, another live runtime owns that socket and Serve refuses to start rather than stealing it. Only when the dial fails is the stale socket file removed and the path reused.

Source

Thrown at proxy/internal/nativeruntime/server_unix.go:46

// ServeUnix exposes one-request-per-connection JSON over a user-only Unix
// socket. Runtime errors close or fail-open the individual call; they never stop
// the coding agent or the provider proxy.
func ServeUnix(ctx context.Context, path string, runtime *Runtime) error {
	if runtime == nil || runtime.store == nil {
		return errors.New("native runtime: store is required")
	}
	if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
		return fmt.Errorf("native runtime mkdir: %w", err)
	}
	if err := os.Chmod(filepath.Dir(path), 0o700); err != nil {
		return fmt.Errorf("native runtime chmod dir: %w", err)
	}
	if _, err := os.Stat(path); err == nil {
		conn, dialErr := net.DialTimeout("unix", path, 50*time.Millisecond)
		if dialErr == nil {
			_ = conn.Close()
			return errors.New("native runtime: socket already active")
		}
		if err := os.Remove(path); err != nil {
			return fmt.Errorf("native runtime remove stale socket: %w", err)
		}
	} else if !os.IsNotExist(err) {
		return fmt.Errorf("native runtime inspect socket: %w", err)
	}
	listener, err := net.Listen("unix", path)
	if err != nil {
		return fmt.Errorf("native runtime listen: %w", err)
	}
	defer listener.Close()
	defer os.Remove(path)
	if err := os.Chmod(path, 0o600); err != nil {
		return fmt.Errorf("native runtime chmod socket: %w", err)
	}
	go func() {
		<-ctx.Done()

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Find and stop the existing instance (check the run-state file for its PID) before starting a new one
  2. Use a different home directory for the second instance if concurrent runtimes are intended
  3. If truly stale, ensure the old process is killed; the server will then remove the dead socket itself

Example fix

# before
$ caveman-proxy serve &   # second instance, same home -> socket already active

# after
$ pkill -f caveman-proxy || kill $(cat ~/.caveman/runstate/port-*/pid)
$ caveman-proxy serve &
Defensive patterns

Strategy: validation

Validate before calling

conn, err := net.DialTimeout("unix", socketPath, 50*time.Millisecond)
if err == nil {
    conn.Close()
    return errors.New("another runtime is already serving this home")
}

Try / catch

if err := nativeruntime.Serve(ctx, home, rt); err != nil {
    if strings.Contains(err.Error(), "socket already active") {
        // stop the existing instance via its run-state PID, then restart
    }
}

Prevention

When it happens

Trigger: Starting a second runtime instance with the same home directory while the first is still serving; an orchestrator/supervisor restarting the service before the old process fully exited; running two proxy commands concurrently against the same home.

Common situations: Development running the daemon twice; systemd/docker restart races where the old process lingers; forking a child that inherits and re-serves the same home path.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/f4de5c3222262623. Report an issue: GitHub.