{"record":{"id":"6cef63933b5ee16d","repo":"cilium/cilium","slug":"wal-closed","errorCode":null,"errorMessage":"wal closed","messagePattern":"wal closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/wal/wal.go","lineNumber":113,"sourceCode":"\nfunc (be BatchErrors) Error() string {\n\tvar builder strings.Builder\n\tfor i, e := range be {\n\t\tif i > 0 {\n\t\t\tbuilder.WriteString(\"; \")\n\t\t}\n\t\tbuilder.WriteString(e.Error())\n\t}\n\treturn builder.String()\n}\n\n// Write appends an event to the WAL. Data is flushed to disk before returning.\nfunc (w *Writer[T]) Write(e ...T) error {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\n\tif w.log == nil {\n\t\treturn fmt.Errorf(\"wal closed\")\n\t}\n\n\tvar ba BatchErrors\n\tfor i, e := range e {\n\t\tdata, err := e.MarshalBinary()\n\t\tif err != nil {\n\t\t\tba = append(ba, BatchError{Index: i, Err: err})\n\t\t\tcontinue\n\t\t}\n\n\t\tlv := &lvWriter{w: w.log}\n\t\tif err := lv.Write(data); err != nil {\n\t\t\tba = append(ba, BatchError{Index: i, Err: err})\n\t\t\tcontinue\n\t\t}\n\t}\n\n\t// Ensure the data is flushed to disk.","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/cilium/cilium/blob/ac7b90affa4baf0642e6685319d56907b3a73a6d/pkg/wal/wal.go#L95-L131","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nwal.Close()\nwal.Write(evt) // \"wal closed\"\n// after\nwal.Write(evt)\nwal.Close() // close only after final write","handlingStrategy":"try-catch","validationCode":"// Go: guard before writing\nif wal == nil || wal.Closed() {\n    return errors.New(\"wal writer is closed\")\n}","typeGuard":null,"tryCatchPattern":"if err := wal.Write(evt); err != nil {\n    if strings.Contains(err.Error(), \"wal closed\") {\n        // stop producers / reopen wal\n    }\n    return err\n}","preventionTips":["Stop all writers before calling Close (use WaitGroup/context).","Encapsulate WAL lifecycle in a single owner type.","Test shutdown ordering with -race."],"tags":["wal","closed-writer","storage"],"backgroundTag":"use-after-close","analyzedSha":"ac7b90affa4baf0642e6685319d56907b3a73a6d","analyzedAt":"2026-08-31T18:27:15.868Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}