kubernetes/kops · error
already closed
Error message
already closed
What it means
Before writing each framed object, writeObjectWithTypeCode checks w.f under fileMutex; if it is nil the writer has been closed (Close sets f to nil) and any further write is rejected with "already closed". This is a lifecycle misuse error, not an I/O failure.
Source
Thrown at pkg/otel/otlptracefile/writer.go:129
// writeObjectWithTypeCode is the key function here. We encode and write the object.
// We include a header that identifies the object using the provided typeCode.
func (w *writer) writeObjectWithTypeCode(ctx context.Context, typeCode TypeCode, obj proto.Message) error {
buf, err := proto.Marshal(obj)
if err != nil {
return fmt.Errorf("converting to proto: %w", err)
}
crc32q := crc32.MakeTable(crc32.Castagnoli)
checksum := crc32.Checksum(buf, crc32q)
flags := uint32(0)
w.fileMutex.Lock()
defer w.fileMutex.Unlock()
if w.f == nil {
return fmt.Errorf("already closed")
}
// write the object with a header.
header := make([]byte, 16)
binary.BigEndian.PutUint32(header[0:4], uint32(len(buf)))
binary.BigEndian.PutUint32(header[4:8], checksum)
binary.BigEndian.PutUint32(header[8:12], flags)
binary.BigEndian.PutUint32(header[12:16], uint32(typeCode))
if _, err := w.f.Write(header); err != nil {
return fmt.Errorf("writing header: %w", err)
}
if _, err := w.f.Write(buf); err != nil {
// TODO: Rotate file?
return fmt.Errorf("writing body: %w", err)
}
return nilView on GitHub (pinned to 4c8573c808)
Solutions
- Ensure Shutdown is only called after all tracing activity stops; flush spans before Close.
- Guard writes: recreate the client (and Start) if you need to export again after Close.
- Serialize shutdown ordering so the tracer provider stops producing spans before the file exporter closes.
Example fix
// before client.Shutdown(ctx) exportSpan(ctx, span) // already closed // after exportSpan(ctx, span) client.Shutdown(ctx)
Defensive patterns
Strategy: try-catch
Try / catch
if err := w.writeObject(ctx, obj); err != nil {
if strings.Contains(err.Error(), "already closed") {
// discard the object; the exporter has been shut down
return nil
}
return err
} Prevention
- Flush/force-export spans before calling Close/Shutdown
- Stop span production before closing the writer
- Do not reuse a client after Shutdown; create a new one
- Order shutdown: tracer provider first, exporter last
When it happens
Trigger: Writing spans after client.Shutdown/Close; a background batch exporter flushing queued spans after shutdown completed; concurrent Shutdown racing with in-flight exports.
Common situations: App shutting down while a background exporter still holds buffered spans; tests tearing down the exporter before flushing.
Related errors
- already started
- error opening %q: %w
- writing header: %w
- writing body: %w
- error reading addons file %s: %v
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/093d3614fd95271d.
Report an issue: GitHub.