chenhg5/cc-connect · error
cannot open lock file: %w
Error message
cannot open lock file: %w
What it means
After ensuring the config directory exists, AcquireInstanceLock opens the lock file .<config>.lock with os.OpenFile(O_CREATE|O_RDWR). Any open failure is wrapped as "cannot open lock file: %w". This is an OS-level open error, distinct from the lock-contention error paths.
Source
Thrown at cmd/cc-connect/instance_lock.go:43
// derived from the config path hash. This allows different configs to run simultaneously.
func AcquireInstanceLock(configPath string) (*InstanceLock, error) {
// Create lock file path based on config path
configDir := filepath.Dir(configPath)
configBase := filepath.Base(configPath)
// Use a predictable name based on config filename
lockName := fmt.Sprintf(".%s.lock", configBase)
lockPath := filepath.Join(configDir, lockName)
// Ensure directory exists
if err := os.MkdirAll(configDir, 0755); err != nil {
return nil, fmt.Errorf("cannot create config directory: %w", err)
}
// Open/create the lock file
f, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
return nil, fmt.Errorf("cannot open lock file: %w", err)
}
// Try to acquire exclusive lock (non-blocking)
err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
if err != nil {
// Lock is held by another process
f.Close()
// Try to read PID from lock file for better error message
pid := readPIDFromLockFile(lockPath)
if pid > 0 {
return nil, fmt.Errorf("another cc-connect instance is already running (PID %d) with config %s", pid, configPath)
}
return nil, fmt.Errorf("another cc-connect instance is already running with config %s", configPath)
}
// Write our PID to the lock file for diagnostics
pid := os.Getpid()View on GitHub (pinned to 4000b2338a)
Solutions
- Inspect and fix permissions on the config directory (chmod u+w / chown $USER).
- If .<config>.lock exists as a directory, remove it: rm -rf <configDir>/.<configBase>.lock.
- Check disk space and read-only mount status for the filesystem.
- Run cc-connect as the same user that owns the config directory (avoid mixing sudo runs).
Defensive patterns
Strategy: validation
Validate before calling
lockPath := filepath.Join(filepath.Dir(configPath), "."+filepath.Base(configPath)+".lock")
if fi, err := os.Stat(lockPath); err == nil && fi.IsDir() {
return fmt.Errorf("%s is a directory; remove it", lockPath)
}
test, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
return err
}
test.Close() Try / catch
lock, err := AcquireInstanceLock(configPath)
if err != nil && strings.Contains(err.Error(), "cannot open lock file") {
slog.Error("lock file unwritable — check dir perms/stale directory", "err", err)
os.Exit(1)
} Prevention
- Never run cc-connect with sudo if the config dir belongs to a normal user.
- After crashes, verify .<config>.lock is a regular file, not a directory.
- Monitor disk space on the volume holding the config.
When it happens
Trigger: os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0644) fails: the config directory is not writable, the lock path exists as a directory, or the filesystem is read-only/full.
Common situations: A previous crash left a directory named .config.toml.lock in the config dir; the config dir is owned by root after running once with sudo; read-only /etc or NFS mount.
Understand the failure class
Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.
Related errors
- cannot create config directory: %w
- cannot create config directory: %w
- cannot read sessions directory %s: %w
- read sessions dir: %w
- read existing Agy hooks %s: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/8ac159ad89578149.
Report an issue: GitHub.