larksuite/cli · error

busdiscover: rename pid file: %w

Error message

busdiscover: rename pid file: %w

What it means

WritePIDFile wraps the failure of vfs.Rename when atomically moving bus.pid.tmp over bus.pid. It cleans up the leftover tmp file and releases the alive lock before returning. The rename is what makes the pid file publish atomic; its failure means no pid file was updated.

Source

Thrown at internal/event/adapter/localbus/busdiscover/pidfile.go:57

func WritePIDFile(eventsDir string, pid int) (*Handle, error) {
	if err := vfs.MkdirAll(eventsDir, 0700); err != nil {
		return nil, fmt.Errorf("busdiscover: mkdir %s: %w", eventsDir, err)
	}
	lock := lockfile.New(filepath.Join(eventsDir, aliveLockFileName))
	if err := lock.TryLock(); err != nil {
		return nil, err
	}
	pidPath := filepath.Join(eventsDir, pidFileName)
	tmpPath := pidPath + ".tmp"
	payload := fmt.Sprintf("%d\n%s\n", pid, time.Now().UTC().Format(time.RFC3339))
	if err := vfs.WriteFile(tmpPath, []byte(payload), 0600); err != nil {
		_ = lock.Unlock()
		return nil, fmt.Errorf("busdiscover: write pid tmp: %w", err)
	}
	if err := vfs.Rename(tmpPath, pidPath); err != nil {
		_ = vfs.Remove(tmpPath)
		_ = lock.Unlock()
		return nil, fmt.Errorf("busdiscover: rename pid file: %w", err)
	}
	return &Handle{lock: lock}, nil
}

func readPIDFile(eventsDir string) (int, time.Time, error) {
	pidPath := filepath.Join(eventsDir, pidFileName)
	data, err := vfs.ReadFile(pidPath)
	if err != nil {
		return 0, time.Time{}, err
	}
	lines := strings.SplitN(strings.TrimSpace(string(data)), "\n", 2)
	if len(lines) < 2 {
		return 0, time.Time{}, fmt.Errorf("busdiscover: malformed pid file %s", pidPath)
	}
	pid, err := strconv.Atoi(strings.TrimSpace(lines[0]))
	if err != nil {
		return 0, time.Time{}, fmt.Errorf("busdiscover: malformed pid in %s: %w", pidPath, err)
	}

View on GitHub (pinned to 7fd6ef3c07)

Solutions

  1. Check whether a directory or special file named bus.pid exists in eventsDir and remove/rename it
  2. Inspect the wrapped OS error; EXDEV or EINVAL indicates unusual mounts — keep eventsDir on one normal filesystem
  3. Verify no external process is replacing or locking files inside eventsDir during startup
  4. Retry WritePIDFile; the lock was released so another attempt is safe

Example fix

// before: stale directory blocks the atomic rename
// busdiscover: rename pid file: rename .../bus.pid.tmp .../bus.pid: is a directory
$ find ~/.larkcli/events -name bus.pid -type d -exec rm -rf {} +
// after: WritePIDFile(eventsDir, pid) succeeds
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the target pid path is not occupied by a non-regular file before WritePIDFile
pidPath := filepath.Join(eventsDir, "bus.pid")
if info, err := os.Lstat(pidPath); err == nil && !info.Mode().IsRegular() {
    return fmt.Errorf("%s is %v, not a regular file; remove it first", pidPath, info.Mode())
}

Try / catch

h, err := busdiscover.WritePIDFile(eventsDir, pid)
if err != nil && strings.Contains(err.Error(), "rename pid file") {
    // clean any directory/special file at bus.pid, then retry once
    if info, statErr := os.Lstat(filepath.Join(eventsDir, "bus.pid")); statErr == nil && !info.Mode().IsRegular() {
        os.RemoveAll(filepath.Join(eventsDir, "bus.pid"))
        h, err = busdiscover.WritePIDFile(eventsDir, pid)
    }
}

Prevention

When it happens

Trigger: Calling WritePIDFile where the tmp-to-final rename fails: bus.pid exists as a directory, cross-device rename (tmp and target on different filesystems via unusual mounts), or target path became unwritable between mkdir and rename.

Common situations: A directory named bus.pid left behind by an odd tool or manual mistake, an overlay/odd mount splitting the dir, or an external watcher replacing the eventsDir contents concurrently.

Related errors


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/299b74025c4d3514. Report an issue: GitHub.