dagger/dagger · warning

closing link: %w

Error message

closing link: %w

What it means

Tracer.Close iterates all attached eBPF links and aggregates any Close() failures into a multi-error wrapped with "closing link: %w". A link Close failure means the kernel refused to detach one of the loaded BPF programs from its hook. This is rarely fatal since process exit cleans up links anyway, but signals a kernel/program lifecycle anomaly.

Source

Thrown at engine/ebpf/filetracer/tracer.go:313

		}
		t.logEvent(&event)
		return nil
	})
}

// Close releases all resources held by the tracer.
func (t *Tracer) Close() error {
	var errs []error

	if t.reader != nil {
		if err := t.reader.Close(); err != nil {
			errs = append(errs, fmt.Errorf("closing ring buffer: %w", err))
		}
	}

	for _, l := range t.links {
		if err := l.Close(); err != nil {
			errs = append(errs, fmt.Errorf("closing link: %w", err))
		}
	}

	t.objs.Close()

	if len(errs) > 0 {
		return errors.Join(errs...)
	}
	return nil
}

// opName returns the human-readable name for an operation.
func opName(op uint32, flags uint32) string {
	switch op {
	case opMount:
		return "MOUNT"
	case opUmount:
		return "UMOUNT"

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Ensure Close() is called exactly once (use sync.Once or check a closed flag).
  2. Close the tracer before releasing the eBPF objects so links are still valid at detach time.
  3. Log and continue: at process exit the kernel auto-detaches links, so this can often be treated as non-fatal.
  4. Upgrade the kernel/cilium/ebpf if detach consistently fails on a specific kernel version.

Example fix

// before
tracer.Close()
tracer.Close() // second call hits already-closed links

// after
var closeOnce sync.Once
closeOnce.Do(func() { _ = tracer.Close() })
Defensive patterns

Strategy: try-catch

Try / catch

if err := tracer.Close(); err != nil {
	var multi interface{ Unwrap() []error }
	if errors.As(err, &multi) {
		for _, e := range errors.Unwrap(err).([]error) { log.Warn("cleanup", "err", e) }
	} else {
		log.Warn("tracer close", "err", err)
	}
}

Prevention

When it happens

Trigger: Calling Close() on an ovltracer/filetracer Tracer when link.Close() (cilium/ebpf link.Link) returns an error, e.g. the link was already closed or the kernel rejected the detach.

Common situations: Double-Close of the same Tracer from concurrent code paths or defer plus explicit Close; kernel versions with buggy detach for kprobes/tracepoints; links whose underlying program object was already released via t.objs.Close() ordering issues.

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/5f7fb6c8360dbf46. Report an issue: GitHub.