49

I am getting a bad file descriptor when trying to append to a logging file within my go routine.

write ./log.log: bad file descriptor

The file exists and has 666 for permissions. At first I thought well maybe it is because each one of them is trying to open the file at the same time. I implemented a mutex to try and avoid that but got the same issue so I removed it.

logCh := make(chan string, 150)
go func() {
    for {
        msg, ok := <-logCh
        if ok {
            if f, err := os.OpenFile("./log.log", os.O_APPEND, os.ModeAppend); err != nil {
                panic(err)
            } else {
                logTime := time.Now().Format(time.RFC3339)
                if _, err := f.WriteString(logTime + " - " + msg); err != nil {
                    fmt.Print(err)
                }
                f.Close()
            }
        } else {
            fmt.Print("Channel closed! \n")
            break
        }
    }
}()
Jared Mackey
  • 3,998
  • 4
  • 31
  • 50

3 Answers3

108

You need to add the O_WRONLY flag :

if f, err := os.OpenFile("./log.log", os.O_APPEND|os.O_WRONLY, os.ModeAppend); err != nil { /*[...]*/ }

To explain, here is the linux documentation for open: http://man7.org/linux/man-pages/man2/openat.2.html :

The argument flags must include one of the following access modes: O_RDONLY, O_WRONLY, or O_RDWR. These request opening the file read- only, write-only, or read/write, respectively.

If you check /usr/local/go/src/syscall/zerrors_linux_amd64.go:660, you can see that:

O_RDONLY                         = 0x0
O_RDWR                           = 0x2
O_WRONLY                         = 0x1

So by default you get a read-only file descriptor.

HectorJ
  • 5,814
  • 3
  • 34
  • 52
  • 2
    This solved my problem, except in my case I had `os.O_WRONLY` in the `os.OpenFile` method in a wrapper function, and I was calling that from elsewhere and trying to read from the file it returned, but of course it was set to write-only so I was getting the same error. Fixed by changing to `os.O_RDWR`. – Lansana Camara Feb 09 '17 at 20:16
  • After almost 7 years, this answer is still useful! – dragonfly02 Oct 25 '22 at 10:41
3

This appears to be a discrepancy between windows and linux. On windows os.O_APPEND implies write access, as can be seen in the code in syscall_windows.go.

if mode&O_APPEND != 0 {
    access &^= GENERIC_WRITE
    access |= FILE_APPEND_DATA
}

in linux the openflags are passed as-is to a linux syscall

So in DOS/WINDOWS you do not need to explicitly add a write flag with an append flag, as it is implied. (this has been default behaviour since the DOS days) Go in linux does need the extra flag added to work.

(...but imo it should not need this flag, as appending implicitly implies that I want to write. Should I bugreport this to golang ?)

  • 1
    Welcome to StackOverflow! So you are telling that at windows it is enough to use only os.O_APPEND but for linux you need to add os.O_WRONLY explicitly, right? Please extend your answer to help future visitors instantly see solution and call examples (like such piece of code work at Windows but not work on Linux and such piece of code would have identical behavior) – Maxim Sagaydachny Feb 20 '20 at 13:26
  • 1
    @MaximSagaydachny yes in windows just the append flag is enough (updated in post) and thanks :) – Trenjeska Schutte Feb 20 '20 at 14:50
2

it used for me

the code before:

os.OpenFile(fileName, os.O_CREATE|os.O_APPEND, os.ModePerm)

and it occured the error: bad file descriptor,

then i add the os.O_WRONLY into the function

the code after:

os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm)

and it did't occured the problem.

jack wu
  • 21
  • 3