golang/go · error
build output %q already exists and is not an object file
Error message
build output %q already exists and is not an object file
What it means
Thrown by checkDstOverwrite when the build output path exists, is a non-empty regular file, is not recognized as an object file (isObject returns false), and the build is not forced. This protects user files from being clobbered by an accidental `go build -o`.
Source
Thrown at src/cmd/go/internal/work/shell.go:256
// When running as a user with sufficient privileges, we may delete
// even device files, for example, which is not intended.
func mayberemovefile(s string) {
if fi, err := os.Lstat(s); err == nil && !fi.Mode().IsRegular() {
return
}
os.Remove(s)
}
// Be careful about removing/overwriting dst.
// Do not remove/overwrite if dst exists and is a directory
// or a non-empty non-object file.
func checkDstOverwrite(dst string, force bool) error {
if fi, err := os.Stat(dst); err == nil {
if fi.IsDir() {
return fmt.Errorf("build output %q already exists and is a directory", dst)
}
if !force && fi.Mode().IsRegular() && fi.Size() != 0 && !isObject(dst) {
return fmt.Errorf("build output %q already exists and is not an object file", dst)
}
}
return nil
}
// writeFile writes the text to file.
func (sh *Shell) writeFile(file string, text []byte) error {
if cfg.BuildN || cfg.BuildX {
switch {
case len(text) == 0:
sh.ShowCmd("", "echo -n > %s # internal", file)
case bytes.IndexByte(text, '\n') == len(text)-1:
// One line. Use a simpler "echo" command.
sh.ShowCmd("", "echo '%s' > %s # internal", bytes.TrimSuffix(text, []byte("\n")), file)
default:
// Use the most general form.
sh.ShowCmd("", "cat >%s << 'EOF' # internal\n%sEOF", file, text)
}View on GitHub (pinned to b6b368adc5)
Solutions
- Choose a fresh output filename that does not collide with existing data.
- Rename or back up the existing file before building.
- Confirm the existing file is safe to overwrite, then force: `go build -o notes.txt` is not forceable in user space — instead delete it first (`rm notes.txt`) or use a different name.
- Verify isObject expectations: the check only auto-overwrites recognized object/binary files.
Example fix
# before // go build -o run.sh (run.sh exists, non-empty, not an object) # after // mv run.sh run.sh.bak && go build -o run.sh
Defensive patterns
Strategy: validation
Validate before calling
// Refuse to clobber non-object files
out := flag.Arg(0)
if fi, err := os.Stat(out); err == nil && fi.Mode().IsRegular() && fi.Size() != 0 && !isObject(out) {
log.Fatalf("%s exists and is not an object file; rename or remove first", out)
} Type guard
func isObject(p string) bool {
f, err := os.Open(p)
if err != nil { return false }
defer f.Close()
var magic [4]byte
n, _ := f.Read(magic[:])
if n < 4 { return false }
// ELF \x7fELF, Mach-O \xfe\xed\xfa\xce/\xcf, PE "MZ"
return bytes.Equal(magic[:4], []byte{0x7f, 'E', 'L', 'F'}) ||
magic[0] == 'M' && magic[1] == 'Z'
} Prevention
- Do not point -o at existing scripts/notes/data.
- Use a dedicated ./bin path for build outputs.
- Back up files before reusing a name for -o.
When it happens
Trigger: Run `go build -o notes.txt` (or any existing non-object file) without force; os.Stat finds a regular file with Size()!=0, isObject(dst) is false, force is false, so the error fires.
Common situations: Reusing a filename that holds a README/script/data file; CI sharing an output name with an artifact from another tool; pointing `-o` at an existing shell script.
Related errors
- build output %q already exists and is a directory
- value is neither 'auto' nor a valid bool
- copying %s: %w
- copying %s to %s: %v
- %v is not a regular file
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/56d1c6133bb3f3fa.
Report an issue: GitHub.