lima-vm/lima · critical

c.Paths.SocketVMNet is empty

Error message

c.Paths.SocketVMNet is empty

What it means

Within StartCmd's SocketVMNet branch, the code re-checks that c.Paths.SocketVMNet is non-empty before formatting the command, panicking with 'c.Paths.SocketVMNet is empty' if it is. This is a defense-in-depth assertion: normally IsDaemonInstalled would have panicked earlier, so reaching here implies an inconsistent config state (e.g. IsDaemonInstalled behavior changed or config mutated between calls).

Solutions

  1. Ensure Paths.SocketVMNet is populated from networks.yaml before building a Config programmatically
  2. Do not mutate Config fields after validation; treat Config as read-only
  3. Construct the config via the normal loader so paths are validated once

Example fix

// before
cfg := &networks.Config{} // SocketVMNet path empty
panicCmd := cfg.StartCmd("shared", networks.SocketVMNet)
// after
cfg, err := networks.LoadConfig() // populates Paths.SocketVMNet from networks.yaml
Defensive patterns

Strategy: validation

Validate before calling

if cfg.Paths.SocketVMNet == "" {
    return fmt.Errorf("config invalid: paths.socketVMNet must be set")
}

Try / catch

func safeStartCmd(cfg *networks.Config, name, daemon string) (cmd string, err error) {
    defer func() { if r := recover(); r != nil { err = fmt.Errorf("%v", r) } }()
    return cfg.StartCmd(name, daemon), nil
}

Prevention

When it happens

Trigger: StartCmd is called on a Config whose Paths.SocketVMNet field is the empty string but which somehow passed the earlier IsDaemonInstalled check — i.e. config mutated concurrently or constructed inconsistently.

Common situations: Programmatically built Config structs that bypass networks.yaml validation; race where the config is reloaded between the check and command construction; hand-assembled Config in tests.

Related errors


AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01). Data as JSON: /api/errors/deee2182450e1614. Report an issue: GitHub.

Appendix: source

Thrown at pkg/networks/commands.go:107

		return osutil.LookupUser("root")
	}
	return osutil.User{}, fmt.Errorf("daemon %#q not defined", daemon)
}

func (c *Config) MkdirCmd() string {
	return fmt.Sprintf("/bin/mkdir -m 775 -p %s", c.Paths.VarRun)
}

func (c *Config) StartCmd(name, daemon string) string {
	if ok, _ := c.IsDaemonInstalled(daemon); !ok {
		panic(fmt.Errorf("daemon %#q is not available", daemon))
	}
	var cmd string
	switch daemon {
	case SocketVMNet:
		nw := c.Networks[name]
		if c.Paths.SocketVMNet == "" {
			panic("c.Paths.SocketVMNet is empty")
		}
		cmd = fmt.Sprintf("%s --pidfile=%s --socket-group=%s --vmnet-mode=%s",
			c.Paths.SocketVMNet, c.PIDFile(name, SocketVMNet), c.Group, nw.Mode)
		switch nw.Mode {
		case ModeBridged:
			cmd += fmt.Sprintf(" --vmnet-interface=%s", nw.Interface)
		case ModeHost, ModeShared:
			cmd += fmt.Sprintf(" --vmnet-gateway=%s --vmnet-dhcp-end=%s --vmnet-mask=%s",
				nw.Gateway, nw.DHCPEnd, nw.NetMask)
		}
		cmd += " " + c.Sock(name)
	default:
		panic(fmt.Errorf("unexpected daemon %#q", daemon))
	}
	return cmd
}

func (c *Config) StopCmd(name, daemon string) string {

View on GitHub (pinned to dd909d0973)