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_db with your target database type before first crawler execution
  • Verify SAVE_DATA_OPTION in config/base_config.py matches 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →