kataras/iris · critical
err
Error message
err
What it means
AccessLog's mustOpenFile panics if os.OpenFile fails to open (or create) the log file with read-write, append and 0600 permissions (middleware/accesslog/accesslog.go:342). The original os error is the panic value, so the message text equals the OS error string.
Source
Thrown at middleware/accesslog/accesslog.go:342
// It panics on error.
func File(path string) *AccessLog {
f := mustOpenFile(path)
return New(bufio.NewReadWriter(bufio.NewReader(f), bufio.NewWriter(f)))
}
// FileUnbuffered same as File but it does not buffer the data,
// it flushes the loggers contents as soon as possible.
func FileUnbuffered(path string) *AccessLog {
f := mustOpenFile(path)
return New(f)
}
func mustOpenFile(path string) *os.File {
// Note: we add os.RDWR in order to be able to read from it,
// some formatters (e.g. CSV) needs that.
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
panic(err)
}
return f
}
// Broker creates or returns the broker.
// Use its `NewListener` and `CloseListener`
// to listen and unlisten for incoming logs.
//
// Should be called before serve-time.
func (ac *AccessLog) Broker() *Broker {
ac.mu.Lock()
if ac.broker == nil {
ac.broker = newBroker()
}
ac.mu.Unlock()
return ac.brokerView on GitHub (pinned to 7bedaf55a0)
Solutions
- Read the wrapped OS error in the panic output and fix the root cause (create the directory with os.MkdirAll, fix permissions).
- Create parent directories before calling accesslog.File: os.MkdirAll(filepath.Dir(path), 0755).
- Open the file yourself with os.OpenFile and pass the *os.File to accesslog.New instead of using the must-style helper.
- Run the process with a user that has write access to the log path.
Example fix
// before
ac := accesslog.New(accesslog.File("./logs/app.log")) // panics if ./logs missing
// after
os.MkdirAll("./logs", 0755)
f, err := os.OpenFile("./logs/app.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
if err != nil { log.Fatal(err) }
ac := accesslog.New(f) Defensive patterns
Strategy: fallback
Validate before calling
// Pre-check the log path is writable
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return err
}
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
if err != nil {
return fmt.Errorf("log file unavailable: %w", err)
} Try / catch
// Wrap accesslog.File with recover + fallback to stdout
func safeFile(path string) (f *os.File) {
defer func() {
if r := recover(); r != nil {
log.Printf("cannot open %s: %v; using stdout", path, r)
f = os.Stdout
}
}()
return accesslog.File(path)
} Prevention
- Create parent directories before opening log files.
- Open files yourself and pass the handle to accesslog.New for explicit error handling.
- Verify the process user has write permission on the log path, especially in containers.
- Avoid logging to read-only filesystems; mount writable volumes.
When it happens
Trigger: Calling accesslog.New(f) with f := accesslog.File(path) or FileUnbuffered(path) where the directory does not exist, the process lacks write permission, or the path is a directory.
Common situations: Logging to a path under a non-existent directory (e.g. ./logs/app.log without a logs dir), running in a read-only container/filesystem, or a permission-denied path under restricted credentials.
Related errors
- accesslog: SetFormatter called with nil Writer
- err
- dest directory: %s: eval symlinks: %w
- unexpected "fsOrDir" argument type of %T (string or fs.FS or
- cannot chmod %#o for %q: %w
AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30).
Data as JSON: /api/errors/85b772559e9d5cf8.
Report an issue: GitHub.