{"record":{"id":"703104f35934d980","repo":"open-webui/open-webui","slug":"database-password-is-required-when-using-sqlite-sq","errorCode":null,"errorMessage":"DATABASE_PASSWORD is required when using sqlite+sqlcipher:// URLs","messagePattern":"DATABASE_PASSWORD is required when using sqlite\\+sqlcipher:// URLs","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"backend/open_webui/internal/db.py","lineNumber":244,"sourceCode":"        return url.replace('postgresql+psycopg2://', 'postgresql+psycopg://', 1)\n    if url.startswith('postgresql://'):\n        return url.replace('postgresql://', 'postgresql+psycopg://', 1)\n    if url.startswith('postgres://'):\n        return url.replace('postgres://', 'postgresql+psycopg://', 1)\n    # For other dialects, return as-is and let SQLAlchemy handle it\n    return url\n\n\n# ============================================================\n# SYNC ENGINE (used only for: startup migrations, config loading,\n#              Alembic, peewee migration, health checks)\n# ============================================================\n\n# Handle SQLCipher URLs\nif SQLALCHEMY_DATABASE_URL.startswith('sqlite+sqlcipher://'):\n    database_password = os.environ.get('DATABASE_PASSWORD')\n    if not database_password or database_password.strip() == '':\n        raise ValueError('DATABASE_PASSWORD is required when using sqlite+sqlcipher:// URLs')\n\n    # Extract database path from SQLCipher URL\n    db_path = SQLALCHEMY_DATABASE_URL.replace('sqlite+sqlcipher://', '')\n\n    # Create a custom creator function that uses sqlcipher3\n    def create_sqlcipher_connection():\n        import sqlcipher3\n\n        conn = sqlcipher3.connect(db_path, check_same_thread=False)\n        conn.execute(f\"PRAGMA key = '{database_password}'\")\n        return conn\n\n    # The dummy \"sqlite://\" URL would cause SQLAlchemy to auto-select\n    # SingletonThreadPool, which non-deterministically closes in-use\n    # connections when thread count exceeds pool_size, leading to segfaults\n    # in the native sqlcipher3 C library. Use NullPool by default for safety,\n    # or QueuePool if DATABASE_POOL_SIZE is explicitly configured.\n    if isinstance(DATABASE_POOL_SIZE, int) and DATABASE_POOL_SIZE > 0:","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/open-webui/open-webui/blob/01f4282f1ffe0d6212f58d3afbeae21fffd0c4be/backend/open_webui/internal/db.py#L226-L262","documentation":"SQLCipher branch of the sync engine setup in backend/open_webui/internal/db.py: when SQLALCHEMY_DATABASE_URL starts with sqlite+sqlcipher://, the code must execute \"PRAGMA key = '<DATABASE_PASSWORD>'\" to decrypt the database file. An unset, empty, or whitespace-only DATABASE_PASSWORD raises ValueError immediately, since opening an encrypted DB without a key is meaningless.","triggerScenarios":"DATABASE_URL=sqlite+sqlcipher:///data/webui.db with DATABASE_PASSWORD unset, empty (DATABASE_PASSWORD=\"\" in .env counts as empty), or containing only whitespace.","commonSituations":".env file defines the variable but with no value; secret injection failing so os.environ lacks it; whitespace/newline artifacts in the value; user expecting a prompt instead of hard failure.","solutions":["Set DATABASE_PASSWORD to the SQLCipher key: a non-empty string, e.g. DATABASE_PASSWORD=$(openssl rand -hex 32) stored securely.","Verify it reaches the process (docker exec <container> printenv DATABASE_PASSWORD) and is not just set in the shell that built the image.","Keep the same key for the life of the database file — losing it loses the data."],"exampleFix":"# before\nDATABASE_URL=sqlite+sqlcipher:///data/webui.db\n# DATABASE_PASSWORD unset\n\n# after\nDATABASE_URL=sqlite+sqlcipher:///data/webui.db\nDATABASE_PASSWORD=<strong-random-key>","handlingStrategy":"validation","validationCode":"import os\n\nif os.environ.get('DATABASE_URL', '').startswith('sqlite+sqlcipher://'):\n    pw = os.environ.get('DATABASE_PASSWORD', '')\n    assert pw and pw.strip(), 'DATABASE_PASSWORD must be a non-empty key for sqlcipher URLs'","typeGuard":null,"tryCatchPattern":"try:\n    import open_webui.internal.db  # noqa\nexcept ValueError as e:\n    raise SystemExit(f'Database init failed: {e}')  # missing key is fatal, do not retry","preventionTips":["Store the SQLCipher key in a secret manager and inject it as DATABASE_PASSWORD.","Back up the key with the database backup — it is required to open the file."],"tags":["database","sqlcipher","encryption","configuration","secret"],"backgroundTag":null,"analyzedSha":"01f4282f1ffe0d6212f58d3afbeae21fffd0c4be","analyzedAt":"2026-08-14T18:25:22.715Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}