SQLAlchemy connection closed error in Telegram bot application

I’m working on a Telegram bot using SQLAlchemy for database operations and keep running into this frustrating error:

sqlalchemy.exc.ResourceClosedError: This Connection is closed

I suspect the issue is related to how I’m managing database sessions, but I can’t figure out the proper way to handle this. The error happens randomly when the bot tries to interact with the database.

Here’s my current implementation:

db_engine = create_engine(database_config['connection_string'])
db_connection = db_engine.connect()
SessionFactory = sessionmaker(bind=db_engine)
db_session = None

telegram_bot = telebot.TeleBot(bot_config['api_token'])
web_app = flask.Flask(__name__)

@web_app.route('/webhook', methods=['POST'])
def handle_webhook():
    if flask.request.headers.get('content-type') == 'application/json':
        global db_session
        db_session = SessionFactory()
        request_data = flask.request.get_data().decode('utf-8')
        bot_update = telebot.types.Update.de_json(request_data)
        telegram_bot.process_new_updates([bot_update])
        return ''
    else:
        flask.abort(403)

@telegram_bot.message_handler(func=lambda msg: msg.chat.id == bot_config['target_chat'], content_types=['text'])
def process_message(msg):
    sender_id = msg.from_user.id
    existing_record = db_session.query(MessageRecord).filter(
        MessageRecord.msg_id == msg.message_id).count()
    if existing_record == 0:
        expiry_time = datetime.now().replace(second=0, microsecond=0) + \
            timedelta(seconds=bot_config['message_duration'])
        if msg.pinned_message != None:
            expiry_time = None
        new_message = MessageRecord(
            sender_id=None, order_id=None, msg_id=msg.message_id, expires_at=expiry_time)
        db_session.add(new_message)
        db_session.commit()

telegram_bot.remove_webhook()
telegram_bot.set_webhook(url=bot_config['webhook_endpoint'],
                certificate=open(bot_config['ssl_certificate'], 'r'))
web_app.run()

What’s the correct way to manage SQLAlchemy sessions in this setup to avoid connection issues?

Been there. You’re fighting SQLAlchemy’s connection management instead of working with it.

Database connections have lifespans - they timeout, get recycled, or drop when your load balancer shuffles traffic. SQLAlchemy tries to handle this but fails when you hold sessions too long.

I spent hours debugging similar connection drops in production bots. The usual fixes (session per request, proper cleanup, connection pooling tweaks) work but need constant maintenance.

What fixed it for me? Moving the entire bot workflow to a platform that handles database connections natively. Each webhook becomes a workflow trigger with fresh database connections, executes your logic, and cleans up automatically.

No session lifecycle management. No connection pool tuning. Database operations happen in isolated steps that retry when connections fail.

Same Telegram webhook processing and database queries, minus the SQLAlchemy headaches. Your message processing logic stays the same but runs way more reliably.

Check it out: https://latenode.com

Connection pooling isn’t your only problem - you’re missing transaction rollbacks when things go wrong. If db_session.commit() fails, your session stays dirty and SQLAlchemy won’t let you reuse it. Wrap your database code in try/except blocks and call session.rollback() on failures before you close the session.

I see what’s happening. Your session management is a mess - you’re creating global sessions that go stale and close unexpectedly.

Main problem: you create one session per webhook request but never close it properly. Database connections timeout and your code doesn’t handle it.

Honestly? Skip the session lifecycle nightmare entirely. Move this to an automation platform instead. I’ve built similar Telegram bots that needed reliable database ops, and manually managing connection pooling, sessions, and error recovery was hell.

What actually worked: bot logic as automated workflows. Each message trigger gets a fresh database connection, processes data, closes cleanly. No global sessions, no connection headaches.

The platform handles reliability - retries when connections fail, proper cleanup, auto-scaling when your bot gets busy. You can visually see workflow steps too, which beats digging through stack traces.

Same logic, webhook triggers and database operations, zero SQLAlchemy session management: https://latenode.com

The Problem:

You’re experiencing sqlalchemy.exc.ResourceClosedError: This Connection is closed errors in your Telegram bot, which uses SQLAlchemy for database operations. The error occurs randomly when the bot interacts with the database, suggesting a problem with how you manage database sessions. Your current implementation uses a global db_session variable, leading to connection issues.

:thinking: Understanding the “Why” (The Root Cause):

The core issue lies in your global db_session variable. In your current setup, a single database session is created and reused across multiple requests handled by your Flask application. This single session is susceptible to issues like connection timeouts or accidental closure. When a connection is closed (e.g., due to inactivity or a database server restart), subsequent attempts to use the db_session result in the ResourceClosedError. Each request to your webhook should ideally use its own, independent database session to prevent conflicts and avoid stale connections.

:gear: Step-by-Step Guide:

Step 1: Replace Global Session with Per-Request Sessions:

Refactor your code to create and manage database sessions within the scope of each request. This prevents the reuse of potentially closed or stale sessions. Use SQLAlchemy’s context manager (async with) to ensure proper session cleanup, even if errors occur.

import asyncio
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker
import telebot
import flask
from datetime import datetime, timedelta

# ... (Your other imports and configurations) ...

async def create_session():
    engine = create_async_engine(database_config['connection_string'], pool_recycle=3600, pool_pre_ping=True, connect_args={'connect_timeout': 10})  #Added connection parameters
    async_session = sessionmaker(engine, expire_on_commit=False, class_=AsyncSession)
    async with async_session() as session:
        yield session

telegram_bot = telebot.TeleBot(bot_config['api_token'])
web_app = flask.Flask(__name__)

@web_app.route('/webhook', methods=['POST'])
async def handle_webhook():
    if flask.request.headers.get('content-type') == 'application/json':
        request_data = flask.request.get_data().decode('utf-8')
        bot_update = telebot.types.Update.de_json(request_data)
        async with create_session() as session:
            await process_updates(session, [bot_update])
        return ''
    else:
        flask.abort(403)


async def process_updates(session, updates):
    for update in updates:
        await process_message(session, update)

async def process_message(session, msg):
    sender_id = msg.from_user.id
    existing_record = await session.execute(
        session.query(MessageRecord).filter(
            MessageRecord.msg_id == msg.message_id
        ).statement
    )
    existing_record = existing_record.scalar() #Return the count as an integer

    if existing_record == 0:
        expiry_time = datetime.now().replace(second=0, microsecond=0) + \
            timedelta(seconds=bot_config['message_duration'])
        if msg.pinned_message != None:
            expiry_time = None
        new_message = MessageRecord(
            sender_id=None, order_id=None, msg_id=msg.message_id, expires_at=expiry_time)
        session.add(new_message)
        await session.commit()


# ... (rest of your code, including webhook setup) ...

if __name__ == "__main__":
    web_app.run()

Step 2: Error Handling and rollback():

Wrap your database interactions in try...except blocks to catch potential exceptions. If an exception occurs during a transaction, use session.rollback() before closing the session to ensure data consistency.

Step 3: Verify Database Configuration:

Double-check your database connection string (database_config['connection_string']) and ensure your database server is running and accessible. Incorrect credentials or network issues can cause connection problems. Consider adding parameters to create_async_engine like pool_recycle and pool_pre_ping to improve connection handling.

:mag: Common Pitfalls & What to Check Next:

  • Incorrect Database URL: Verify that your database connection string is correct, including the host, port, database name, username, and password. A single typo can lead to connection failures.
  • Database Server Issues: Check the status of your database server. If the server is down or unreachable, your bot will be unable to connect.
  • Deadlocks: In a high-traffic environment, database deadlocks can occur. Examine your database queries for potential deadlock scenarios and optimize them if necessary.
  • Connection Pool Exhaustion: If your application receives a large number of concurrent requests, your database connection pool might get exhausted. Increase the pool size if necessary. Review the connection parameters in create_async_engine.

:speech_balloon: Still running into issues? Share your (sanitized) config files, the exact command you ran, and any other relevant details. The community is here to help!

Your global session setup is causing connection pooling problems. Database connections timeout when idle, and SQLAlchemy won’t automatically reconnect when they drop.

I hit this same issue with a Flask bot a few months ago. Fixed it by ditching the global session approach and using proper session lifecycle management instead. Don’t create one session per webhook - make fresh sessions inside each message handler and dispose them right after.

Add pool_recycle=3600 and pool_pre_ping=True to your create_engine call. Pre_ping tests connections before using them and reconnects automatically if they’re dead. Also wrap your DB operations in try-except blocks to catch disconnection errors and retry with new sessions.

Most important: always call session.close() in a finally block after your DB work finishes. Your code creates sessions but never closes them, so you’re slowly exhausting the connection pool.

You’re mixing direct connection management with session-based operations. You create db_connection but never actually use it - then rely on sessions that reference the same engine. When the connection drops from timeouts or network issues, your session dies.

I hit this same problem with a high-volume Telegram support bot. Fixed it by ditching the global session approach for session-per-operation with context managers. Make a helper function that gives you fresh sessions and cleans up automatically.

Also add connect_args={'connect_timeout': 10} to your engine config. Database servers love closing idle connections without telling you, and this catches dead connections faster. Right now you’re creating sessions with zero error recovery when connections go stale between webhook calls.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.