cilium/cilium · error
wal closed
Error message
wal closed
What it means
Writer.Write returns "wal closed" when the WAL's underlying log file is nil, meaning the WAL has been closed (or never initialized) and can no longer accept appends. It guards against writing to a closed file handle after Close or a failed open. Writes are rejected immediately instead of panicking on a nil resource.
Source
Thrown at pkg/wal/wal.go:113
func (be BatchErrors) Error() string {
var builder strings.Builder
for i, e := range be {
if i > 0 {
builder.WriteString("; ")
}
builder.WriteString(e.Error())
}
return builder.String()
}
// Write appends an event to the WAL. Data is flushed to disk before returning.
func (w *Writer[T]) Write(e ...T) error {
w.mu.Lock()
defer w.mu.Unlock()
if w.log == nil {
return fmt.Errorf("wal closed")
}
var ba BatchErrors
for i, e := range e {
data, err := e.MarshalBinary()
if err != nil {
ba = append(ba, BatchError{Index: i, Err: err})
continue
}
lv := &lvWriter{w: w.log}
if err := lv.Write(data); err != nil {
ba = append(ba, BatchError{Index: i, Err: err})
continue
}
}
// Ensure the data is flushed to disk.View on GitHub (pinned to ac7b90affa)
Solutions
- Ensure Close() is called only after all producer goroutines have stopped writing (use WaitGroup/context cancellation).
- Re-create or re-open the WAL writer before writing again after a Close.
- Check application startup logs for a failed WAL open that left the writer uninitialized.
- Serialize writes through a single owner so no code path writes after shutdown begins.
Example fix
// before wal.Close() wal.Write(evt) // "wal closed" // after wal.Write(evt) wal.Close() // close only after final write
Defensive patterns
Strategy: try-catch
Validate before calling
// Go: guard before writing
if wal == nil || wal.Closed() {
return errors.New("wal writer is closed")
} Try / catch
if err := wal.Write(evt); err != nil {
if strings.Contains(err.Error(), "wal closed") {
// stop producers / reopen wal
}
return err
} Prevention
- Stop all writers before calling Close (use WaitGroup/context).
- Encapsulate WAL lifecycle in a single owner type.
- Test shutdown ordering with -race.
When it happens
Trigger: Calling Write(e...) on a *Writer[T] whose log field is nil — i.e. after Close() was called, or the writer was constructed/initialized without successfully opening the log file.
Common situations: Shutdown ordering bugs where a producer goroutine keeps appending after the WAL was closed during teardown; reusing a writer across restarts without re-creating it; a failed Open/Init leaving a writer with a nil log.
Related errors
AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31).
Data as JSON: /api/errors/6cef63933b5ee16d.
Report an issue: GitHub.