How to Troubleshoot "No Such Table" Database Initialization Errors in MediaCrawler
MediaCrawler raises "no such table" errors when the SQLAlchemy ORM tables have not been created before data insertion, typically because the --init_db flag was not used or the SAVE_DATA_OPTION configuration points to a different database type.
MediaCrawler relies on SQLAlchemy to manage persistence across multiple media platforms including Bilibili, Weibo, Douyin, and others. The "no such table" error surfaces when your crawler attempts to write or query data against an uninitialized database schema. Below is a complete diagnostic walkthrough based on the actual source code in the NanmiCoder/MediaCrawler repository.
Understanding the Database Bootstrap Flow
MediaCrawler's persistence layer follows a strict initialization sequence. Understanding this flow helps pinpoint exactly where your setup diverged.
Step 1: CLI Argument Parsing
The entry point in main/main.py checks for the --init_db argument at lines 104-106:
# From main/main.py
if args.init_db:
await db.init_db(args.init_db)
else:
# Falls back to config.SAVE_DATA_OPTION
await db.init_db(config.SAVE_DATA_OPTION)
Without this flag, the system attempts initialization using your global configuration—which may default to jsonl (file-based storage) rather than a relational database.
Step 2: Database Initialization Dispatch
The database/db.py module provides the public init_db() function:
# From database/db.py (lines 46-48)
async def init_db(db_type: str):
"""Initialize database tables for the specified engine type"""
await init_table_schema(db_type)
This function forwards to schema creation logic based on your chosen database type: sqlite, mysql, or postgres.
Step 3: Table Schema Creation
The heavy lifting occurs in database/db_session.py (lines 77-85):
# From database/db_session.py
async def create_tables(db_type: str):
# Ensure database exists (for MySQL/PostgreSQL)
await create_database_if_not_exists(db_type)
# Build async engine and create all ORM tables
engine = get_async_engine(db_type)
async with engine.begin() as conn:
# Creates tables defined in models.py
await conn.run_sync(Base.metadata.create_all)
This is where Base.metadata.create_all materializes tables like bilibili_video, weibo_note, and douyin_aweme based on the declarative models in database/models.py.
Step 4: Engine Selection
The get_async_engine() function (lines 63-70) constructs the appropriate connection URL:
- SQLite:
sqlite+aiosqlite:///path/to/sqlite_tables.db - MySQL:
mysql+asyncmy://user:pass@host/db - PostgreSQL:
postgresql+asyncpg://user:pass@host/db
If SAVE_DATA_OPTION is not one of these three values, this function returns None, and the initialization cascade fails silently or produces connection errors.
Common Causes of "No Such Table" Errors
| Cause | Diagnostic Signature | Solution |
|---|---|---|
Missing --init_db execution |
Error occurs on first data insertion; database file may not exist | Run CLI with --init_db sqlite (or mysql/postgres) |
Wrong SAVE_DATA_OPTION |
Config set to jsonl prevents database engine initialization |
Edit config/base_config.py to set SAVE_DATA_OPTION = "sqlite" |
| Incorrect SQLite path | File exists but is 0 bytes; tables absent despite --init_db |
Verify config/db_config.py points to writable directory |
| Schema drift after model changes | New model added to database/models.py but table missing |
Re-run --init_db after any ORM model modification |
Step-by-Step Resolution
1. Verify Your Database Configuration
Check the active persistence option before attempting initialization:
from config.base_config import SAVE_DATA_OPTION
print("Current save option:", SAVE_DATA_OPTION)
Valid values are:
"sqlite"— Local file-based database"mysql"— Remote MySQL server"postgres"— Remote PostgreSQL server"jsonl"— Not a database; outputs line-delimited JSON files
If this prints jsonl, your crawler will never create database tables regardless of --init_db usage.
2. Initialize Tables via CLI
Execute the explicit initialization command:
# For SQLite (most common local setup)
python -m MediaCrawler.main --init_db sqlite
# For MySQL
python -m MediaCrawler.main --init_db mysql
# For PostgreSQL
python -m MediaCrawler.main --init_db postgres
This triggers await db.init_db('sqlite'), which cascades through init_table_schema() → create_tables() → Base.metadata.create_all().
3. Validate Table Existence
Confirm tables were actually created using SQLAlchemy's inspection API:
from sqlalchemy import inspect, create_engine
from config.db_config import sqlite_db_config
# Build synchronous engine for inspection
engine = create_engine(f"sqlite:///{sqlite_db_config['db_path']}")
inspector = inspect(engine)
tables = inspector.get_table_names()
print(f"Found {len(tables)} tables: {tables}")
# Expected output includes:
# ['bilibili_video', 'weibo_note', 'douyin_aweme',
# 'kuaishou_video', 'xhs_note', 'zhihu_answer', ...]
If this returns an empty list, the initialization failed silently—check file permissions and engine configuration.
4. Programmatic Initialization (Python API)
For embedded usage or custom scripts, invoke initialization directly:
import asyncio
from database import db
async def init_sqlite():
"""Explicitly initialize SQLite tables from Python code"""
await db.init_db('sqlite')
# Verify success
print("Database initialized successfully")
asyncio.run(init_sqlite())
5. Safe Insertion Pattern with Verification
Defensive code can verify table presence before operations:
import asyncio
from database.db_session import get_session
from database.models import WeiboNote
async def safe_insert():
async with get_session() as session:
if session is None:
raise RuntimeError(
"No DB engine configured. "
"Check SAVE_DATA_OPTION and run --init_db"
)
# Probes table existence; raises OperationalError if missing
await session.run_sync(
lambda conn: conn.execute('SELECT 1 FROM weibo_note LIMIT 1')
)
# Proceed with insertion
note = WeiboNote(
creator_hash='demo_hash',
nickname='demo_user',
add_ts=1699999999,
note_id='W123456789',
content='Verified insert after table check',
create_time=1699999999,
)
session.add(note)
await session.commit()
asyncio.run(safe_insert())
Key Source Files Reference
| File | Purpose | Critical Functions |
|---|---|---|
database/db.py |
Public database API | init_db(), close() |
database/db_session.py |
Engine & session management | get_async_engine(), create_tables(), get_session() |
database/models.py |
ORM table definitions | All Base subclasses (BilibiliVideo, WeiboNote, etc.) |
config/db_config.py |
Database connection parameters | sqlite_db_config, mysql_db_config, postgres_db_config |
config/base_config.py |
Global persistence setting | SAVE_DATA_OPTION |
main/main.py |
CLI entry point | Argument parsing, init_db trigger |
cmd_arg/arg.py |
Argument definitions | --init_db flag specification |
Summary
- Always run
--init_dbwith your target database type before first crawler execution - Verify
SAVE_DATA_OPTIONinconfig/base_config.pymatches your intended storage mechanism - Re-initialize after model changes—adding fields or tables requires fresh
Base.metadata.create_all()execution - Inspect table presence using SQLAlchemy's
inspect()when debugging silent failures - Check file permissions for SQLite paths in
config/db_config.py
Frequently Asked Questions
What does the "no such table" error mean in MediaCrawler?
The error indicates your crawler attempted to insert or query data against a database where the SQLAlchemy ORM tables have not been created. In database/db_session.py, the create_tables() function must execute Base.metadata.create_all() before any data operations. Without this step—typically triggered by --init_db—the underlying SQLite file or MySQL/PostgreSQL database exists but contains no tables.
Can I use MediaCrawler without a database?
Yes. Set SAVE_DATA_OPTION = "jsonl" in config/base_config.py to output line-delimited JSON files instead of using SQLAlchemy. This bypasses all database initialization and avoids "no such table" errors entirely. Note that you cannot use --init_db with this option, as no database engine is configured.
Why does --init_db fail silently with no tables created?
Silent failures usually indicate get_async_engine() in database/db_session.py returned None due to an unrecognized db_type. Confirm your argument matches exactly: sqlite, mysql, or postgres (not postgresql or SQLITE). Also verify config/db_config.py contains valid connection parameters—invalid credentials for MySQL/PostgreSQL may fail without visible error in some async contexts.
How do I add a new table to MediaCrawler's database?
Define your model in database/models.py using SQLAlchemy's declarative syntax with a proper __tablename__. Then re-run python -m MediaCrawler.main --init_db [your_db_type]. The existing create_tables() logic in database/db_session.py automatically picks up new Base subclasses and generates corresponding tables through Base.metadata.create_all().
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →