wavetermdev/waveterm · error

gofmt not found at %s: %w

Error message

gofmt not found at %s: %w

What it means

ResolveGoFmtPath locates the gofmt binary next to the configured/discovered Go toolchain (settings.TsunamiGoPath, falling back to build.FindGoExecutable) and stats <goDir>/gofmt[.exe]. If os.Stat fails, the error is wrapped as 'gofmt not found at %s'. It means the Go installation directory does not contain a gofmt binary.

Source

Thrown at pkg/waveapputil/waveapputil.go:51

	if goPath == "" {
		var err error
		goPath, err = build.FindGoExecutable()
		if err != nil {
			return "", err
		}
	}

	goDir := filepath.Dir(goPath)
	gofmtName := "gofmt"
	if runtime.GOOS == "windows" {
		gofmtName = "gofmt.exe"
	}
	gofmtPath := filepath.Join(goDir, gofmtName)

	info, err := os.Stat(gofmtPath)
	if err != nil {
		return "", fmt.Errorf("gofmt not found at %s: %w", gofmtPath, err)
	}

	if info.IsDir() {
		return "", fmt.Errorf("gofmt path is a directory: %s", gofmtPath)
	}

	if info.Mode()&0111 == 0 {
		return "", fmt.Errorf("gofmt is not executable: %s", gofmtPath)
	}

	return gofmtPath, nil
}

func FormatGoCode(contents []byte) []byte {
	gofmtPath, err := ResolveGoFmtPath()
	if err != nil {
		return contents
	}

View on GitHub (pinned to a4447c1563)

Solutions

  1. Check the resolved path in the error and install/move gofmt there (it ships with the Go toolchain)
  2. Fix the TsunamiGoPath setting to point to a real Go installation (directory containing go and gofmt)
  3. Run 'which go' / 'go env GOROOT' and verify gofmt exists at GOROOT/bin/gofmt
  4. Reinstall Go if the toolchain is incomplete

Example fix

// before
// settings.TsunamiGoPath = "/opt/old-go/bin/go" (stale, gofmt missing)
// after
// settings.TsunamiGoPath = "/usr/local/go/bin/go"
// ls /usr/local/go/bin  ->  go  gofmt
Defensive patterns

Strategy: validation

Validate before calling

goPath := settings.TsunamiGoPath
if goPath == "" { goPath, _ = build.FindGoExecutable() }
gofmt := filepath.Join(filepath.Dir(goPath), gofmtName())
if _, err := os.Stat(gofmt); err != nil {
    return fmt.Errorf("precheck: %w", err)
}

Type guard

func gofmtAvailable() bool {
    p, err := waveapputil.ResolveGoFmtPath()
    return err == nil && p != ""
}

Try / catch

// FormatGoCode silently falls back to unformatted content on error;
// call ResolveGoFmtPath directly to surface the problem:
if _, err := waveapputil.ResolveGoFmtPath(); err != nil {
    log.Printf("gofmt unavailable, formatting disabled: %v", err)
}

Prevention

When it happens

Trigger: Calling FormatGoFile/FormatGoCode when gofmt is absent from the resolved Go bin directory: os.Stat returns ENOENT (file missing) or the path is otherwise unstattable (broken symlink, permission on parent dir).

Common situations: TsunamiGoPath setting points at a non-Go directory or a stale path after a Go upgrade/uninstall; only the go binary was copied without the rest of the toolchain; partial Go installation (gofmt removed by antivirus or cleanup tool); PATH finds 'go' but bin dir is incomplete.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/a02574960e7de3aa. Report an issue: GitHub.