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.broker

View on GitHub (pinned to 7bedaf55a0)

Solutions

  1. Read the wrapped OS error in the panic output and fix the root cause (create the directory with os.MkdirAll, fix permissions).
  2. Create parent directories before calling accesslog.File: os.MkdirAll(filepath.Dir(path), 0755).
  3. Open the file yourself with os.OpenFile and pass the *os.File to accesslog.New instead of using the must-style helper.
  4. 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

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


AI-assisted analysis of kataras/iris@7bedaf55a0 (2026-08-30). Data as JSON: /api/errors/85b772559e9d5cf8. Report an issue: GitHub.