Skip to content

[Bug] Exception upserting into sessions table: Object of type Message is not JSON serializableΒ #5581

@Vortex375

Description

@Vortex375

Description

The PreHookStartedEvent resp. PreHookCompletedEvent contain a run_input object which in turn contains input_content. Usually the input_content is a string, but when using Teams and the team member agent has add_history_to_context=True, then input_content can also be a list of Message objects.

Furthermore, neither PreHookStartedEvent nor PreHookCompletedEvent override the to_dict() method of the base class, so when to_dict() is called, the run_input is not converted to dict. The PostgresDb calls to_dict() to convert the entire run data to JSON for persistence, but since input_content is not converted to dict, it can not be persisted as JSON.

This causes breakage when using PostgresDB and Agno tries to persist these events into the database at the end of the run:

ERROR    Exception upserting into sessions table: Object of type Message is not JSON serializable 

Steps to Reproduce

To reproduce this you need a Team with an Agent that has add_history_to_context=True and also has a pre_hook or post_hook installed (as otherwise no PreHookStartedEvent resp. PreHookCompletedEvent events are generated).

Here is a minimal code example:

from agno.agent import Agent
from agno.guardrails import BaseGuardrail
from agno.db.postgres import AsyncPostgresDb
from agno.models.openai import OpenAIResponses
from agno.team import Team
from agno.os import AgentOS
from agno.tools.mcp import MCPTools
from dotenv import load_dotenv

load_dotenv()

db_url = "postgresql+psycopg_async://agno:agno@localhost:5432/agno"
db = AsyncPostgresDb(db_url=db_url)

class TestGuardrail(BaseGuardrail):
    def check(self, run_input):
        print(f"Guardrail {run_input}")

    async def async_check(self, run_input):
        print(f"Async Guardrail {run_input}")

# Create the Agent
agno_agent = Agent(
    name="Agno Agent",
    model=OpenAIResponses(id="gpt-5.1"),
    role="Expert on Agno framework",
    db=db,
    pre_hooks=[TestGuardrail()],
    # Add the Agno MCP server to the Agent
    tools=[MCPTools(transport="streamable-http", url="https://docs.agno.com/mcp")],
    # Add the previous session history to the context
    add_history_to_context=True,
    markdown=True,
)

agno_team = Team(
    name="Agno Team",
    members=[agno_agent],
    instructions="DO NOT RESPOND DIRECTLY. ALWAYS delegate to agno-agent, then summarize the results before responding to the user.",
    db=db,
    model=OpenAIResponses(id="gpt-5.1"),
    add_history_to_context=True,
)

# Create the AgentOS
agent_os = AgentOS(teams=[agno_team])
# Get the FastAPI app for the AgentOS
app = agent_os.get_app()

To trigger the issue, you need to execute at least TWO runs in the same session, so that the Team adds the history of the previous run (as Message objects) to the Agent's input.

Run the app and execute this twice:

curl -X 'POST' \
    'http://127.0.0.1:8000/teams/agno-team/runs' \
    -H 'accept: application/json' \
    -H 'Content-Type: multipart/form-data' \
    -F 'message=Explain what Agno is' \
    -F 'stream=true' \
    -F 'session_id=test'

Agent Configuration (if applicable)

No response

Expected Behavior

The run including all events is persisted to the database.

Actual Behavior

The run data is lost and not persisted to the DB. This loss of data is almost silent - the only thing that gets logged to the console is

ERROR    Exception upserting into sessions table: Object of type Message is not JSON serializable

Screenshots or Logs (if applicable)

No response

Environment

-Agno Version: 2.3.4

Possible Solutions (optional)

No response

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions