fix: add proactive DB writability check before init_db writes

sqlite3.connect() opens read-only files without error — the failure
only surfaces on the first write (INSERT). Add an upfront os.access()
check on both the directory and file, with a clear error showing the
owner and the exact chown command to fix it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-03-02 10:03:11 +00:00
parent c35131462e
commit e756a00cc9

View File

@@ -62,11 +62,49 @@ def get_db():
raise
def _check_db_writable(db_path: Path) -> None:
"""Verify the database file and directory are writable, raising a clear
error with fix instructions if they are not."""
import os
# Check directory writability (needed for SQLite journal/WAL files)
db_dir = db_path.parent
if db_dir.exists() and not os.access(db_dir, os.W_OK):
owner = _file_owner(db_dir)
msg = (
f"Database directory {db_dir} is not writable (owned by {owner}). "
f"Fix with: sudo chown -R $(whoami) {db_dir}"
)
logger.error(msg)
raise sqlite3.OperationalError(msg)
# Check file writability if it already exists
if db_path.exists() and not os.access(db_path, os.W_OK):
owner = _file_owner(db_path)
msg = (
f"Database {db_path} is not writable (owned by {owner}). "
f"Fix with: sudo chown -R $(whoami) {db_dir}"
)
logger.error(msg)
raise sqlite3.OperationalError(msg)
def _file_owner(path: Path) -> str:
"""Return the owner username of a file, or UID if lookup fails."""
try:
import pwd
return pwd.getpwuid(path.stat().st_uid).pw_name
except (ImportError, KeyError):
return str(path.stat().st_uid)
def init_db() -> None:
"""Initialize the database schema."""
db_path = get_db_path()
logger.info(f"Initializing database at {db_path}")
_check_db_writable(db_path)
with get_db() as conn:
# Settings table for key-value storage
conn.execute('''