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 nil

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Ensure Shutdown is only called after all tracing activity stops; flush spans before Close.
  2. Guard writes: recreate the client (and Start) if you need to export again after Close.
  3. 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

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


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/093d3614fd95271d. Report an issue: GitHub.