{"record":{"id":"f4e93d49624a0d36","repo":"larksuite/cli","slug":"busdiscover-write-pid-tmp-w","errorCode":null,"errorMessage":"busdiscover: write pid tmp: %w","messagePattern":"busdiscover: write pid tmp: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/event/adapter/localbus/busdiscover/pidfile.go","lineNumber":52,"sourceCode":"\treturn h.lock.Unlock()\n}\n\n// WritePIDFile takes the alive lock and atomically writes pid + RFC3339 start time.\n// Returns lockfile.ErrHeld if another bus holds the lock.\nfunc WritePIDFile(eventsDir string, pid int) (*Handle, error) {\n\tif err := vfs.MkdirAll(eventsDir, 0700); err != nil {\n\t\treturn nil, fmt.Errorf(\"busdiscover: mkdir %s: %w\", eventsDir, err)\n\t}\n\tlock := lockfile.New(filepath.Join(eventsDir, aliveLockFileName))\n\tif err := lock.TryLock(); err != nil {\n\t\treturn nil, err\n\t}\n\tpidPath := filepath.Join(eventsDir, pidFileName)\n\ttmpPath := pidPath + \".tmp\"\n\tpayload := fmt.Sprintf(\"%d\\n%s\\n\", pid, time.Now().UTC().Format(time.RFC3339))\n\tif err := vfs.WriteFile(tmpPath, []byte(payload), 0600); err != nil {\n\t\t_ = lock.Unlock()\n\t\treturn nil, fmt.Errorf(\"busdiscover: write pid tmp: %w\", err)\n\t}\n\tif err := vfs.Rename(tmpPath, pidPath); err != nil {\n\t\t_ = vfs.Remove(tmpPath)\n\t\t_ = lock.Unlock()\n\t\treturn nil, fmt.Errorf(\"busdiscover: rename pid file: %w\", err)\n\t}\n\treturn &Handle{lock: lock}, nil\n}\n\nfunc readPIDFile(eventsDir string) (int, time.Time, error) {\n\tpidPath := filepath.Join(eventsDir, pidFileName)\n\tdata, err := vfs.ReadFile(pidPath)\n\tif err != nil {\n\t\treturn 0, time.Time{}, err\n\t}\n\tlines := strings.SplitN(strings.TrimSpace(string(data)), \"\\n\", 2)\n\tif len(lines) < 2 {\n\t\treturn 0, time.Time{}, fmt.Errorf(\"busdiscover: malformed pid file %s\", pidPath)","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/internal/event/adapter/localbus/busdiscover/pidfile.go#L34-L70","documentation":"WritePIDFile wraps the failure of vfs.WriteFile when writing the temporary pid file (bus.pid.tmp, mode 0600) inside the already-created eventsDir. On failure it releases the alive lock before returning so the lock is not leaked. The underlying OS error is preserved via %w.","triggerScenarios":"Calling WritePIDFile after the eventsDir was created and the lock acquired, but the tmp write fails: disk full, quota exceeded, permission loss on the directory, or the directory removed concurrently by another process.","commonSituations":"Disk full on the state partition (ENOSPC), another process or cleaner deleting the events dir between mkdir and write, SELinux/AppArmor denying writes, or a read-only remount of the state volume.","solutions":["Inspect the wrapped cause: ENOSPC/EDQUOT means free disk space or raise quota on the state volume","Check the eventsDir still exists and is writable (ls -ld); recreate it if a cleaner removed it","Retry WritePIDFile after transient conditions; the lock is released so reacquisition is safe","Look for external cleanup jobs or security policies interfering with the config/state directory"],"exampleFix":"// before: writing to a volume that is full\nerr := busdiscover.WritePIDFile(eventsDir, pid) // fails: busdiscover: write pid tmp: ... no space left\n// after: preflight the volume before starting the bus\nif stat, err := os.Statvfs(eventsDir); err != nil || stat.Bavail == 0 { return fmt.Errorf(\"state volume full\") }\nerr := busdiscover.WritePIDFile(eventsDir, pid)","handlingStrategy":"retry","validationCode":"// Preflight: confirm eventsDir exists and accepts writes before WritePIDFile\nif info, err := os.Stat(eventsDir); err != nil || !info.IsDir() {\n    return fmt.Errorf(\"events dir %s missing\", eventsDir)\n}\nprobe := filepath.Join(eventsDir, \".write-probe\")\nif err := os.WriteFile(probe, []byte(\"ok\"), 0600); err != nil {\n    return fmt.Errorf(\"events dir %s not writable: %w\", eventsDir, err)\n}\nos.Remove(probe)","typeGuard":null,"tryCatchPattern":"var h *busdiscover.Handle\nvar err error\nfor i := 0; i < 3; i++ {\n    h, err = busdiscover.WritePIDFile(eventsDir, pid)\n    if err == nil || !errors.As(err, new(*fs.PathError)) {\n        break\n    }\n    time.Sleep(200 * time.Millisecond) // transient ENOSPC/cleaner race\n}","preventionTips":["Keep free space headroom on the state volume; alert on ENOSPC/EDQUOT","Exclude the CLI state directory from aggressive cleanup jobs (tmpwatch, disk cleaners)","Avoid read-only remounts or SELinux/AppArmor denials on the state path","Release/reacquire is safe on this failure — the lock is unlocked before the error returns"],"tags":["go","filesystem","write-file","pidfile","localbus"],"backgroundTag":"disk-full-write-failed","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}