2 октября 2026 г.
sqlite3.OperationalError: unable to open database file и Read-only file system: куда бот может писать файлы
Бот стартует и тут же падает на подключении к базе: sqlite3.OperationalError: unable to open database file. Или при сохранении JSON: OSError: [Errno 30] Read-only file system, PermissionError: [Errno 13]. Локально всё работало, база создавалась сама.
Что значит эта ошибка
SQLite хранит базу в обычном файле. Чтобы его открыть или создать, процессу нужно право писать в папку, где этот файл лежит. unable to open database file — это не «база сломана», а «не могу создать или открыть файл по этому пути». Папки нет, путь не тот или писать туда нельзя.
Реальный пример:
DB_PATH = "data/users.db"
...
File "/app/01bot.py", line 42, in init_db
conn = sqlite3.connect(DB_PATH)
sqlite3.OperationalError: unable to open database fileПричины по убыванию вероятности
- Папка из пути не существует. SQLite создаёт файл, но не создаёт папки. Дома
data/когда-то создали руками, а в git пустые папки не попадают, поэтому на сервере её нет. - Писать рядом с кодом нельзя. На нормальном хостинге код бота лежит на файловой системе только для чтения: так бот не может испортить сам себя, а каждый деплой начинается с чистого кода. Любая запись рядом с
.pyфайлами даётRead-only file systemили ту жеunable to open database file. - Относительный путь считается не от того места.
"bot.db"отсчитывается от текущей папки процесса, а не от папки скрипта. Запустили из другой директории, и база создалась (или не создалась) не там. - Нет прав. Файл или папка принадлежат другому пользователю (часто после запуска через
sudo).
Как исправить
- Вынесите путь в переменную окружения с разумным значением по умолчанию:
import os DB_PATH = os.getenv("DB_PATH", "/data/bot.db") os.makedirs(os.path.dirname(DB_PATH), exist_ok=True) conn = sqlite3.connect(DB_PATH)makedirsзакрывает первую причину, а переменная позволяет дома указатьDB_PATH=bot.db, не трогая код. - То же самое со всем, что бот пишет: JSON со списком пользователей, загруженные фото, логи в файл.
DATA_DIR = os.getenv("DATA_DIR", "/data") USERS_FILE = os.path.join(DATA_DIR, "users.json") - Если хотите путь «рядом со скриптом» (для локального запуска), стройте его от
__file__, а не от текущей папки:BASE = os.path.dirname(os.path.abspath(__file__)) DB_PATH = os.path.join(BASE, "bot.db")
Но на сервере с read-only кодом это всё равно не сработает, там нужна отдельная папка для данных. - Для aiosqlite, SQLAlchemy (
sqlite:////data/bot.db, четыре слеша — абсолютный путь), peewee и прочих правило то же.
Как это устроено в botdepo
Код бота доступен только для чтения. Писать можно в одну папку, /data: она сохраняется между деплоями и перезапусками, поэтому база не обнуляется при каждом обновлении кода. Укажите путь внутри /data, как в примере выше, и всё. Если бот упадёт на записи файла, botdepo распознает это в логе и подскажет, куда перенести путь. Задеплоить бота.
Частые вопросы
Почему бы не закоммитить bot.db в репозиторий?
База поменяется с первым же пользователем, и следующий деплой затрёт её версией из git. Плюс в публичном репозитории окажутся данные ваших пользователей. Добавьте *.db в .gitignore.
SQLite вообще нормально для бота?
Для бота на сотни и тысячи пользователей — да, вполне. Главное, чтобы файл лежал в постоянном месте и бот был запущен в одном экземпляре.