Skip to content

[Bug] Race condition in ChainlitDataLayer causes Steps to lose content in History #2789

@Pranjalya

Description

@Pranjalya

Describe the bug
When using a database via DATABASE_URL (Postgres), content within cl.Step occasionally disappears or appears empty when reloading a thread from history. This is caused by a race condition between the initial step.send() and the subsequent step.update().

To Reproduce
It is caused because cl.Step initializes input and output as empty strings (""). In ChainlitDataLayer.create_step, and the SQL uses COALESCE(EXCLUDED.output, "Step".output). Since empty string is not NULL, Postgres accepts the empty string as a valid update. If the background task for the initial send finishes after the final update, the content is overwritten by the empty string.

Script to reproduce this issue:

import os
import asyncio
import chainlit as cl
from chainlit.data.chainlit_data_layer import ChainlitDataLayer

# Force the race condition by delaying empty saves
original_create_step = ChainlitDataLayer.create_step
async def laggy_create_step(self, step_dict):
    if step_dict.get("output") == "":
        await asyncio.sleep(2) # Task A waits
    return await original_create_step(self, step_dict)
ChainlitDataLayer.create_step = laggy_create_step

@cl.on_message
async def send_message():
    async with cl.Step(name="VulnerableStep") as step:
        await asyncio.sleep(0.1)
        step.output = "This content will be lost"
    await cl.Message(content="Refresh the page and check history.").send()

Set DATABASE_URL and authentication to enable threads. Perform chainlit run, send a message, then refresh the page.

Expected behavior
Step content should be persistent. The data layer should not allow an empty string to overwrite existing content in the database.

Additional context
Current behaviour: If the initial "start" task (Task A) commits to the DB after the "end" task (Task B), the step becomes empty in history.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions