{"record":{"id":"e3515ee29eb4c9e3","repo":"lima-vm/lima","slug":"expected-hacmd-stderr-to-be-os-file-got-t","errorCode":null,"errorMessage":"expected haCmd.Stderr to be *os.File, got %T","messagePattern":"expected haCmd\\.Stderr to be \\*os\\.File, got %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/instance/start_unix.go","lineNumber":27,"sourceCode":"\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"syscall\"\n\n\t\"github.com/mattn/go-isatty\"\n\t\"github.com/sirupsen/logrus\"\n\n\t\"github.com/lima-vm/lima/v2/pkg/osutil\"\n)\n\nfunc execHostAgentForeground(limactl string, haCmd *exec.Cmd) error {\n\thaStdoutW, ok := haCmd.Stdout.(*os.File)\n\tif !ok {\n\t\treturn fmt.Errorf(\"expected haCmd.Stdout to be *os.File, got %T\", haCmd.Stdout)\n\t}\n\thaStderrW, ok := haCmd.Stderr.(*os.File)\n\tif !ok {\n\t\treturn fmt.Errorf(\"expected haCmd.Stderr to be *os.File, got %T\", haCmd.Stderr)\n\t}\n\tlogrus.Info(\"Running the host agent in the foreground\")\n\tif isatty.IsTerminal(os.Stdin.Fd()) || isatty.IsCygwinTerminal(os.Stdin.Fd()) {\n\t\t// Write message to standard log files to avoid confusing users\n\t\tmessage := \"This log file is not used because `limactl start` was launched in the terminal with the `--foreground` option.\"\n\t\tif _, err := haStdoutW.WriteString(message); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif _, err := haStderrW.WriteString(message); err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tif err := osutil.Dup2(int(haStdoutW.Fd()), syscall.Stdout); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := osutil.Dup2(int(haStderrW.Fd()), syscall.Stderr); err != nil {\n\t\t\treturn err\n\t\t}","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/lima-vm/lima/blob/dd909d0973cd84fa35f9e1693181b4585ea616c1/pkg/instance/start_unix.go#L9-L45","documentation":"Same invariant as the Stdout check but for haCmd.Stderr: foreground execution of the hostagent needs real *os.File descriptors. A non-file writer passed to haCmd.Stderr triggers this error before the hostagent is exec'd in the foreground.","triggerScenarios":"Internal code path (or patch) assigning a non-*os.File writer (pipe, buffer, multiwriter) to haCmd.Stderr before execHostAgentForeground runs.","commonSituations":"Modified limactl builds; not seen with official binaries.","solutions":["Restore haCmd.Stderr to an *os.File assignment in the calling code","Use an official limactl binary","In patches, use os.Pipe and log from the read end in a goroutine"],"exampleFix":"// before\nhaCmd.Stderr = osutil.NewLoggerWriter(...) // not *os.File\n// after\nhaStderr, _ := os.OpenFile(haStderrPath, os.O_CREATE|os.O_WRONLY, 0o644)\nhaCmd.Stderr = haStderr\n","handlingStrategy":"type-guard","validationCode":"if _, ok := haCmd.Stderr.(*os.File); !ok {\n    return fmt.Errorf(\"callers must set haCmd.Stderr to *os.File\")\n}","typeGuard":"func ensureStderrIsFile(cmd *exec.Cmd) (*os.File, bool) {\n    f, ok := cmd.Stderr.(*os.File)\n    return f, ok\n}","tryCatchPattern":"if err := execHostAgentForeground(limactl, haCmd); err != nil {\n    if strings.Contains(err.Error(), \"expected haCmd.Stderr\") {\n        return fmt.Errorf(\"internal wiring bug: %w\", err)\n    }\n    return err\n}","preventionTips":["Keep haCmd.Stderr as *os.File in all start paths","For log capture in patches, drain an os.Pipe in a goroutine instead of swapping the writer","Test foreground mode on unix in CI"],"tags":["internal-invariant","hostagent","unix"],"backgroundTag":"unexpected-internal-state","analyzedSha":"dd909d0973cd84fa35f9e1693181b4585ea616c1","analyzedAt":"2026-09-01T14:24:59.842Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}