{"record":{"id":"d4001ede552c6c06","repo":"lima-vm/lima","slug":"expected-hacmd-stdout-to-be-os-file-got-t","errorCode":null,"errorMessage":"expected haCmd.Stdout to be *os.File, got %T","messagePattern":"expected haCmd\\.Stdout to be \\*os\\.File, got %T","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/instance/start_unix.go","lineNumber":23,"sourceCode":"\npackage instance\n\nimport (\n\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","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/lima-vm/lima/blob/dd909d0973cd84fa35f9e1693181b4585ea616c1/pkg/instance/start_unix.go#L5-L41","documentation":"execHostAgentForeground requires haCmd.Stdout to be an *os.File so it can be passed through / reused for foreground execution and log writing. This is an internal invariant: StartWithPaths sets haCmd.Stdout to an *os.File before calling this; failing the assertion means internal wiring changed incorrectly.","triggerScenarios":"Only reachable when the internal code path changes — e.g. a fork/patch that assigns an io.Writer (bytes.Buffer, pipe) to haCmd.Stdout before calling execHostAgentForeground on a platform where foreground mode is implemented (unix).","commonSituations":"Custom builds or patches to lima's start path; end users should never see this unless running modified limactl.","solutions":["Revert local modifications so StartWithPaths keeps assigning *os.File to haCmd.Stdout","Use an unmodified limactl build from an official release","If hacking on lima, wrap the writer with os.Pipe and pass the file end instead"],"exampleFix":"// before\nhaCmd.Stdout = &buf // *bytes.Buffer\n// after\nf, _ := os.CreateTemp(\"\", \"ha-stdout\")\nhaCmd.Stdout = f // *os.File\n","handlingStrategy":"type-guard","validationCode":"if _, ok := haCmd.Stdout.(*os.File); !ok {\n    return fmt.Errorf(\"callers must set haCmd.Stdout to *os.File\")\n}","typeGuard":"func ensureStdoutIsFile(cmd *exec.Cmd) (*os.File, bool) {\n    f, ok := cmd.Stdout.(*os.File)\n    return f, ok\n}","tryCatchPattern":"if err := execHostAgentForeground(limactl, haCmd); err != nil {\n    if strings.Contains(err.Error(), \"expected haCmd.Stdout\") {\n        return fmt.Errorf(\"internal wiring bug: %w\", err)\n    }\n    return err\n}","preventionTips":["When modifying start.go, keep assigning *os.File to haCmd.Stdout/Stderr","Add unit tests asserting the *os.File wiring before foreground exec","Use os.Pipe/file when capturing output in patched builds"],"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"}