Hmbown/CodeWhale · error · NSError(NSPOSIXErrorDomain)

NSPOSIXErrorDomain errno

Error message

NSPOSIXErrorDomain errno

What it means

At PetHabitatStore.swift:92, coordinated(_:) throws Self.ioError() — an NSPOSIXErrorDomain error carrying the live errno — when openat(directory, lockName, O_RDONLY|O_NOFOLLOW|O_NONBLOCK|O_CLOEXEC) fails to reopen the lock file. ENOENT means the lock file was deleted or renamed after the store was initialized; other errno values indicate permission or descriptor problems. The open-with-O_NOFOLLOW validation step exists to detect lock-file tampering before running the critical section.

Solutions

  1. Recreate the store: re-run PetHabitatStore.init, which re-creates the missing `<source>.lock` file (O_CREAT) with 0600 permissions.
  2. Restore the habitat directory contents — stop any sync tool from managing that folder or exclude it from syncing.
  3. Check fd exhaustion with `lsof -p <pid> | wc -l` against the RLIMIT_NOFILE and fix the descriptor leak.
  4. Verify directory permissions remain 0700 and are owned by the current user.

Example fix

// before: keep using a store whose lock file vanished
try store.save(payload)

// after: rebuild the store when the lock file is gone
do { try store.save(payload) }
catch let e as NSError where e.domain == NSPOSIXErrorDomain && e.code == ENOENT {
    store = try PetHabitatStore(directory: habitatURL, source: source)
    try store.save(payload)
}
Defensive patterns

Strategy: fallback

Validate before calling

// Verify the lock file still exists before operating
FileManager.default.fileExists(atPath: url.appendingPathComponent(source + ".lock").path)

Type guard

func isLockFileMissing(_ e: Error) -> Bool {
    let n = e as NSError
    return n.domain == NSPOSIXErrorDomain && n.code == ENOENT
}

Try / catch

do { try store.save(payload) }
catch let e as NSError where e.domain == NSPOSIXErrorDomain && e.code == ENOENT {
    store = try PetHabitatStore(directory: url, source: source) // recreate lock
    try store.save(payload)
}

Prevention

When it happens

Trigger: load() or save() when the `<source>.lock` file has been removed from the habitat directory since PetHabitatStore.init; EACCES/EPERM after directory permissions were tightened; EMFILE when the process has exhausted its file-descriptor limit.

Common situations: A cleanup script or the user manually deletes lock files inside the app-support directory; a sync service (iCloud Drive, Dropbox) replaces or removes the directory contents; too many open files after a leak elsewhere.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/121ba8a331892a8a. Report an issue: GitHub.

Appendix: source

Thrown at pet/swift/PetHabitatStore.swift:92

                let written = Darwin.write(file, bytes.baseAddress!.advanced(by: offset), bytes.count - offset)
                if written < 0 && errno == EINTR { continue }
                guard written > 0 else { throw Self.ioError() }
                offset += written
            }
        }
        guard fsync(file) == 0 else { throw Self.ioError() }
        if immutable {
            if linkat(directory, temporary, directory, name, 0) != 0 {
                guard errno == EEXIST, try read(name: name, limit: 64 * 1024 * 1024) == value else { throw Self.ioError() }
            }
        } else if renameat(directory, temporary, directory, name) != 0 { throw Self.ioError() }
    }

    private func coordinated<T>(_ action: () throws -> T) throws -> T {
        guard flock(lock, LOCK_EX | LOCK_NB) == 0 else { throw Self.ioError() }
        defer { flock(lock, LOCK_UN) }
        let current = openat(directory, lockName, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC)
        guard current >= 0 else { throw Self.ioError() }
        defer { close(current) }
        let original = try Self.regular(lock), now = try Self.regular(current)
        guard original.st_dev == now.st_dev && original.st_ino == now.st_ino else { throw Self.invalid("The habitat writer lock was replaced.") }
        return try action()
    }

    private func read(name key: String? = nil, limit: Int = PetHabitatStore.limit) throws -> Data? {
        let file = openat(directory, key ?? name, O_RDONLY | O_NOFOLLOW | O_NONBLOCK | O_CLOEXEC)
        if file < 0 && errno == ENOENT { return nil }
        guard file >= 0 else { throw Self.ioError() }
        defer { close(file) }
        let info = try Self.regular(file)
        guard info.st_size >= 0 && info.st_size <= limit else { throw Self.invalid("The saved habitat exceeds 8 MiB.") }
        var value = Data(), buffer = [UInt8](repeating: 0, count: 16_384)
        while true {
            let count = Darwin.read(file, &buffer, min(buffer.count, limit + 1 - value.count))
            if count < 0 && errno == EINTR { continue }
            guard count >= 0 else { throw Self.ioError() }

View on GitHub (pinned to 433685b202)