{"record":{"id":"12b430e6106e55df","repo":"multica-ai/multica","slug":"brew-upgrade-failed-w","errorCode":null,"errorMessage":"brew upgrade failed: %w","messagePattern":"brew upgrade failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/internal/cli/update.go","lineNumber":335,"sourceCode":"func GetBrewPrefix() string {\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\tcmd := exec.CommandContext(ctx, \"brew\", \"--prefix\")\n\tcmd.WaitDelay = 2 * time.Second\n\tout, err := cmd.Output()\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn strings.TrimSpace(string(out))\n}\n\n// UpdateViaBrew runs `brew upgrade multica-ai/tap/multica`.\n// Returns the combined output and any error.\nfunc UpdateViaBrew() (string, error) {\n\tcmd := exec.Command(\"brew\", \"upgrade\", \"multica-ai/tap/multica\")\n\tout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn string(out), fmt.Errorf(\"brew upgrade failed: %w\", err)\n\t}\n\treturn string(out), nil\n}\n\nfunc updateDownloadTimeoutOrDefault(timeout time.Duration) time.Duration {\n\tif timeout <= 0 {\n\t\treturn DefaultUpdateDownloadTimeout\n\t}\n\treturn timeout\n}\n\n// fetchURLBytes does a GET with the given timeout and returns the response\n// body in full. Used for the checksum manifest (tiny) and the release\n// archive (single-digit MB). The checksum verification path requires buffered\n// bytes so streaming would just push the buffer into the caller anyway.\nfunc fetchURLBytes(url string, timeout time.Duration) ([]byte, error) {\n\tclient := &http.Client{Timeout: updateDownloadTimeoutOrDefault(timeout)}\n\tresp, err := client.Get(url)","sourceCodeStart":317,"sourceCodeEnd":353,"githubUrl":"https://github.com/multica-ai/multica/blob/2c0912b6ec764b373d44eeea1e80f0d9f11ab417/server/internal/cli/update.go#L317-L353","documentation":"UpdateViaBrew shells out to `brew upgrade multica-ai/tap/multica` and wraps any non-zero exit with 'brew upgrade failed: %w'. The wrapped error is an *exec.ExitError plus the captured CombinedOutput is returned alongside it. This is the self-update path used when the running binary is detected as a Homebrew install (IsBrewInstall).","triggerScenarios":"Running the CLI update command on a machine where Homebrew is not installed or not on PATH; the tap multica-ai/tap no longer exists or was renamed; the formula is already up to date combined with a brew quirk; brew lacking write permission to its prefix (sudo-owned /opt/homebrew or /usr/local); network failure reaching github.com while brew fetches the bottle.","commonSituations":"CI runners with minimal images that lack brew; a user installed via the download path but a brew prefix string accidentally matches MatchKnownBrewPrefix; macOS System Integrity/permission prompts after a macOS upgrade; an offline or firewalled environment.","solutions":["Read the returned output string — brew prints the actual reason (unknown formula, permission, network) to stderr and it is included in the first return value.","Verify Homebrew is installed and on PATH: `which brew` (exec.LookPath(\"brew\") in code).","Fix ownership of the brew prefix (e.g. `sudo chown -R $(whoami) /opt/homebrew`) or rerun the update in a shell where brew works.","Repair the tap: `brew tap multica-ai/tap` then retry the upgrade.","If brew is fundamentally unavailable, fall back to the download-based updater (UpdateViaDownload) instead of the brew path."],"exampleFix":"// before\nout, err := cli.UpdateViaBrew()\nif err != nil {\n    log.Fatal(err) // loses brew's own diagnostics\n}\n\n// after\nout, err := cli.UpdateViaBrew()\nif err != nil {\n    log.Printf(\"brew update failed: %v\\nbrew output:\\n%s\", err, out)\n    if _, lookErr := exec.LookPath(\"brew\"); lookErr != nil {\n        log.Printf(\"falling back to download-based update\")\n        out, err = cli.UpdateViaDownload(targetVersion)\n    }\n}","handlingStrategy":"fallback","validationCode":"if _, err := exec.LookPath(\"brew\"); err != nil {\n    // brew unavailable; do not attempt UpdateViaBrew\n}\nif !cli.IsBrewInstall() {\n    // not a brew-managed binary; use UpdateViaDownload instead\n}","typeGuard":"func isExitError(err error) bool {\n    var ee *exec.ExitError\n    return errors.As(err, &ee)\n}","tryCatchPattern":"out, err := cli.UpdateViaBrew()\nif err != nil {\n    log.Printf(\"brew update failed: %v\\noutput: %s\", err, out)\n    if !isExitError(err) {\n        return err // brew missing/launch failure, not a brew-level failure\n    }\n    out, err = cli.UpdateViaDownload(latest)\n}","preventionTips":["Gate the brew path on cli.IsBrewInstall() before calling UpdateViaBrew","Always log the returned output string — brew's stderr explains the exit","Verify brew is on PATH with exec.LookPath before updating"],"tags":["brew","exec","self-update","homebrew"],"backgroundTag":null,"analyzedSha":"2c0912b6ec764b373d44eeea1e80f0d9f11ab417","analyzedAt":"2026-08-15T13:25:18.241Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}