wavetermdev/waveterm · error

gofmt path is a directory: %s

Error message

gofmt path is a directory: %s

What it means

ResolveGoFmtPath guards against misconfiguration by rejecting a gofmt path that exists but is a directory rather than an executable file, returning 'gofmt path is a directory: %s'. This prevents exec.Command from failing later with a confusing 'permission denied' or 'is a directory' error.

Source

Thrown at pkg/waveapputil/waveapputil.go:55

		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
	}

	cmd := exec.Command(gofmtPath)
	cmd.Stdin = bytes.NewReader(contents)
	formattedOutput, err := cmd.Output()

View on GitHub (pinned to a4447c1563)

Solutions

  1. Remove or rename the directory at the reported path so a real gofmt file can live there
  2. Extract/install the gofmt binary file directly into the Go bin directory
  3. Correct TsunamiGoPath so it points to the go executable, not a directory root
  4. Verify with 'file <path>/gofmt' that it is an executable, not a directory

Example fix

// before
# ls $GOBIN/gofmt   ->  directory (extracted archive)
// after
# rm -rf $GOBIN/gofmt
# cp $(go env GOROOT)/bin/gofmt $GOBIN/gofmt
Defensive patterns

Strategy: validation

Validate before calling

p, err := waveapputil.ResolveGoFmtPath()
if err != nil { return err }
info, _ := os.Stat(p)
if info.IsDir() { return fmt.Errorf("%s is a directory", p) }

Type guard

func isExecutableFile(path string) bool {
    info, err := os.Stat(path)
    return err == nil && info.Mode().IsRegular()
}

Try / catch

if _, err := waveapputil.ResolveGoFmtPath(); err != nil {
    if strings.Contains(err.Error(), "is a directory") {
        log.Printf("misconfigured toolchain: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: os.Stat on <goDir>/gofmt succeeds and info.IsDir() is true — e.g. TsunamiGoPath points to a directory whose sibling layout makes gofmt resolve to a directory, or someone created a folder named 'gofmt' in the Go bin directory.

Common situations: A user created a 'gofmt' directory (e.g. extracted an archive into bin/gofmt/ instead of bin/); TsunamiGoPath misconfigured so filepath.Dir resolves oddly; a mount point named gofmt.

Related errors


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