wavetermdev/waveterm · error

gofmt is not executable: %s

Error message

gofmt is not executable: %s

What it means

After confirming the gofmt path exists and is a file, ResolveGoFmtPath checks the executable bit (info.Mode()&0111). If no execute permission is set, it returns 'gofmt is not executable: %s' rather than letting exec fail later. (On Windows this check is effectively always satisfied.)

Source

Thrown at pkg/waveapputil/waveapputil.go:59

	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()
	if err != nil {
		return contents
	}

View on GitHub (pinned to a4447c1563)

Solutions

  1. chmod +x <gofmt path> on the reported path
  2. Re-copy the binary preserving modes: 'install -m 755 src dst' or extract with a tool that preserves permissions
  3. Check the mount options (noexec) and remount with exec if applicable
  4. Verify with 'ls -l' that the owner has execute permission

Example fix

// before
-rw-r--r-- gofmt
// after
# chmod +x /usr/local/go/bin/gofmt
-rwxr-xr-x gofmt
Defensive patterns

Strategy: validation

Validate before calling

p, err := waveapputil.ResolveGoFmtPath()
if err != nil { return err }
if info, err := os.Stat(p); err == nil && info.Mode()&0111 == 0 {
    return fmt.Errorf("precheck: %s lacks exec bit", p)
}

Type guard

func hasExecBit(path string) bool {
    info, err := os.Stat(path)
    return err == nil && info.Mode()&0111 != 0
}

Try / catch

if _, err := waveapputil.ResolveGoFmtPath(); err != nil {
    if strings.Contains(err.Error(), "not executable") {
        // attempt repair: os.Chmod(path, 0755) if the file is owned by the user
    }
    return err
}

Prevention

When it happens

Trigger: gofmt exists as a regular file at <goDir>/gofmt but has no +x bits — e.g. copied with cp without -p from a non-executable source, extracted from a zip that lost permission bits, or on a mount with noexec/umask stripping exec bits.

Common situations: Home directory mounted with noexec; archive extraction tool dropped the executable bit; file synced via cloud drive or copied from Windows losing Unix modes; overly restrictive umask during install.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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