{"record":{"id":"b211ef9c31183ef1","repo":"Hmbown/CodeWhale","slug":"provider-catalog-lock-must-be-a-regular-file","errorCode":null,"errorMessage":"provider catalog lock {} must be a regular file","messagePattern":"provider catalog lock (.+?) must be a regular file","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/provider_catalog_live.rs","lineNumber":537,"sourceCode":"    #[cfg(unix)]\n    {\n        use std::os::unix::fs::OpenOptionsExt as _;\n        options\n            .mode(0o600)\n            .custom_flags(libc::O_NOFOLLOW | libc::O_CLOEXEC | libc::O_NONBLOCK);\n    }\n    #[cfg(windows)]\n    {\n        use std::os::windows::fs::OpenOptionsExt as _;\n        options.custom_flags(0x0020_0000); // FILE_FLAG_OPEN_REPARSE_POINT\n    }\n    let file = options\n        .open(path)\n        .with_context(|| format!(\"open provider catalog lock {}\", path.display()))?;\n    let metadata = file\n        .metadata()\n        .with_context(|| format!(\"inspect provider catalog lock {}\", path.display()))?;\n    anyhow::ensure!(\n        metadata.is_file(),\n        \"provider catalog lock {} must be a regular file\",\n        path.display()\n    );\n    #[cfg(unix)]\n    {\n        use std::os::unix::fs::MetadataExt as _;\n        anyhow::ensure!(\n            metadata.nlink() == 1,\n            \"provider catalog lock {} must not be hard linked\",\n            path.display()\n        );\n    }\n    #[cfg(windows)]\n    {\n        use std::os::windows::fs::MetadataExt as _;\n        const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x0000_0400;\n        anyhow::ensure!(","sourceCodeStart":519,"sourceCodeEnd":555,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/provider_catalog_live.rs#L519-L555","documentation":"The provider catalog's on-disk cache uses a lock file with advisory locking. Before locking, `open_cache_lock` verifies the path is a regular file; if it is a directory, FIFO, socket, or device node, locking semantics are unsafe and the operation fails loudly rather than silently misbehaving.","triggerScenarios":"`open_cache_lock` (called by `load_from_disk`, `persist_scope`, `persist_failure_scope`) opens the lock path and `metadata.is_file()` is false — e.g. something created a directory at the lock file's path.","commonSituations":"A previous crash or bad tooling created a directory named like the lock file, a container/tmpfs volume mounted at that path, dotfile-sync tools replacing the lock with a symlink/dir, or permission-restricted paths resolving oddly.","solutions":["Inspect the path shown in the message with `ls -la` and remove/replace the non-regular entry (e.g. `rm -rf <lock-path>` if it is a directory).","Restart the operation; the lock file is recreated as a regular file.","Check that no container volume or sync tool is mounted at that path.","If it recurs, clear the whole provider-catalog cache directory."],"exampleFix":"// before: lock path is a directory\n$ ls -la ~/.cache/codewhale/provider-catalog.lock\ndrwxr-xr-x provider-catalog.lock\n// after\n$ rm -rf ~/.cache/codewhale/provider-catalog.lock\n$ codewhale   # lock recreated as a regular file","handlingStrategy":"validation","validationCode":"import stat from 'node:fs';\nconst st = stat.statSync(lockPath);\nif (!st.isFile()) {\n  console.error(`${lockPath} is not a regular file — remove it before running`);\n  process.exit(1);\n}","typeGuard":"const isRegularFile = (p) => { try { return require('fs').statSync(p).isFile(); } catch { return false; } };","tryCatchPattern":"try {\n  await codewhale.providers.refresh();\n} catch (e) {\n  if (e.message.includes('must be a regular file')) {\n    fs.rmSync(lockPath, { recursive: true, force: true });\n    return codewhale.providers.refresh();\n  }\n  throw e;\n}","preventionTips":["Don't mount volumes or point caches at paths where tools create directories.","Exclude the codewhale cache dir from dotfile sync and snapshot tools.","After crashes, check the cache dir for stray directories before retrying."],"tags":["file-locking","cache","rust"],"backgroundTag":"file-open-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}