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
- Recreate the store: re-run PetHabitatStore.init, which re-creates the missing `<source>.lock` file (O_CREAT) with 0600 permissions.
- Restore the habitat directory contents — stop any sync tool from managing that folder or exclude it from syncing.
- Check fd exhaustion with `lsof -p <pid> | wc -l` against the RLIMIT_NOFILE and fix the descriptor leak.
- 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
- Exclude the habitat directory from sync/backup tools that delete lock files.
- Rebuild the store instead of reusing it after external directory changes.
- Keep the directory permissions at 0700 and owned by the app user.
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
- NSPOSIXErrorDomain
- 1
- Codewhale-owned OAuth credentials were not found at
- CodewhalePet/1
- Downloaded release artifacts are missing
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)