netbirdio/netbird · error

rename output file: %w

Error message

rename output file: %w

What it means

The final atomic-publish step of pcap output: os.Rename of the temp file onto the requested --output path failed. The temp file lives in the same directory by design, so cross-device rename (EXDEV) should not occur — the realistic causes are a missing target directory, a target that is an existing directory, or permission issues on the directory/name. The capture data itself is lost from the final path when this fires (the temp file may remain).

Source

Thrown at client/cmd/capture.go:183

	f, err := os.CreateTemp(filepath.Dir(outPath), filepath.Base(outPath)+".*.tmp")
	if err != nil {
		return nil, nil, fmt.Errorf("create output file: %w", err)
	}
	tmpPath := f.Name()
	return f, func() error {
		var merr *multierror.Error
		if err := f.Close(); err != nil {
			merr = multierror.Append(merr, fmt.Errorf("close output file: %w", err))
		}
		fi, statErr := os.Stat(tmpPath)
		if statErr != nil || fi.Size() == 0 {
			if rmErr := os.Remove(tmpPath); rmErr != nil && !os.IsNotExist(rmErr) {
				merr = multierror.Append(merr, fmt.Errorf("remove empty output file: %w", rmErr))
			}
			return nberrors.FormatErrorOrNil(merr)
		}
		if err := os.Rename(tmpPath, outPath); err != nil {
			merr = multierror.Append(merr, fmt.Errorf("rename output file: %w", err))
			return nberrors.FormatErrorOrNil(merr)
		}
		cmd.PrintErrf("Wrote %s\n", outPath)
		return nberrors.FormatErrorOrNil(merr)
	}, nil
}

func handleCaptureError(err error) error {
	if s, ok := status.FromError(err); ok {
		return fmt.Errorf("%s", s.Message())
	}
	return err
}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Confirm the parent directory of -o still exists when the capture ends; for long captures prefer a stable directory (e.g. /var/tmp or $HOME) that tmp cleaners do not purge
  2. Never pass a directory as -o; the value must be a file path
  3. If a stray .tmp file remains next to the target, rename it manually: mv /path/x.pcap.*.tmp /path/x.pcap to salvage the data
  4. Re-run with a shorter --duration to verify the path works, then scale up

Example fix

# before: long capture into a cleaned tmp dir
netbird debug capture -d 24h -o /tmp/long.pcap

# after: durable location
netbird debug capture -d 24h -o /var/tmp/netbird/long.pcap   # mkdir -p /var/tmp/netbird first
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the parent exists and is a real directory, and the target name is not a dir:
dir := filepath.Dir(outPath)
if err := os.MkdirAll(dir, 0o755); err != nil { return err }
if fi, err := os.Stat(outPath); err == nil && fi.IsDir() {
    return fmt.Errorf("%s is a directory", outPath)
}

Try / catch

// After a rename failure, salvage data from the temp file:
if err := os.Rename(tmpPath, outPath); err != nil {
    if data, rerr := os.ReadFile(tmpPath); rerr == nil {
        os.WriteFile(outPath+".recovered", data, 0o644)
    }
    return err
}

Prevention

When it happens

Trigger: The output directory was removed between capture start and finish (tmp cleaner, scripted cleanup of /tmp); --output names an existing directory (netbird debug capture -o /tmp — basename '/tmp.*.tmp' created in / renames onto /tmp); directory not writable for rename (w+x needed) though CreateTemp succeeded via a race; target file exists and is owned by another user on systems where rename over it is denied.

Common situations: Long captures into systemd-tmpfiles-cleaned /tmp: cleaner wipes the dir mid-run; output path supplied by a script that also mkdirs/rms concurrently; -o pointing at a mountpoint root.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/3f89730322fb4226. Report an issue: GitHub.